题解:P16926 「LAOI-13」Deadlocked

· · 题解

题意简述

构造两个长度为 n 的排列 P,Q, 使它们的最长上升子序列长度分别为 x,y

对每个位置连接 P_i,Q_i, 还要求所得无向图恰有 k 个连通块。

解题思路

令置换 S 满足 S(P_i)=Q_i。 每个点在图中的两条关联边都来自 S, 所以图的连通块数就是置换 S 的环数。

一个含 k 个环的置换可以用 n-k 次交换变成恒等置换。 交换排列中的两个位置,LIS 长度至多改变 2。 因此,可行的必要条件为:

|x-y|\le2(n-k)

x,y 都属于 \{1,n\} 时,排列被唯一确定。 若 x=y,只能取 k=n。 若 \{x,y\}=\{1,n\},升序与降序排列之间形成翻转置换, 故只能取 k=\lceil n/2\rceil

除这些边界情形外,上述必要条件也是充分条件。 不妨交换两个排列,使 x\le y

L(l,r)l,l+1,\dots,r, 记 R(r,l)r,r-1,\dots,l。 若左端点超过右端点,就将这一段视为空序列。

一个连通块。k=1y<n 时, 令 d=n-yc=\left\lfloor\frac{y-x+1}{2}\right\rfloor

构造:

P=L(1,x-1),R(n,x)

另一个排列为:

Q=L(2,x),L(x+d,x+d+c-1),1,L(x+d+c,n),R(x+d-1,x+1) 按五个单调段比较首尾值,可得 $Q$ 的 LIS 为 $y$。 沿映射 $P_i\mapsto Q_i$ 依次追踪这些区间, 所有数恰好组成一个置换环。 当 $k=1$ 且 $y=n$ 时,令: $$ c=\left\lfloor\frac{n-x}{2}\right\rfloor $$ 取 $Q=L(1,n)$,并构造: $$ P=L(2,x),R(n,x+c+1),1,R(x+c,x+1) $$ 同样按单调段可直接验证,$P$ 的 LIS 为 $x$, 且 $P$ 自身是一个置换环。 **处理 $x=1$。** 固定 $P=R(n,1)$, 并令 $S$ 为 $Q$ 的逆序。 此时 $Q$ 的 LIS 等于 $S$ 的最长下降子序列长度, 图的连通块数等于 $S$ 的置换环数。 若目标下降子序列长度为 $n-1$,令: $$ t=\left\lceil\frac{n+1}{2}\right\rceil-k $$ 构造: $$ S=(t+1),R(n,t+2),R(t,1) $$ 其最长下降子序列长度为 $n-1$。 追踪首段与两个降序段可得环数为 $k$。 对于其余情况,令 $d=n-y$。 若 $k\le d$,在 $S$ 前加入 $k-1$ 个不动点, 剩余部分归约到 $k=1$。 否则加入 $d-1$ 个不动点, 剩余规模为 $y+1$,归约到下降长度为规模减一的情形。 这些不动点的值小于后缀中的所有值, 不会增加后缀的最长下降子序列, 并且每个不动点恰好增加一个置换环。 **两个小型基形。** 当 $x=y=2$ 时,取: $$ P=1,R(n,2) $$ 若 $k=n$,令 $Q=P$;否则令: $$ Q=2,R(n,n-k+2),1,R(n-k+1,3) $$ 当 $x=2,y=n$ 时,取 $Q=L(1,n)$。 若 $k=\lceil(n+1)/2\rceil$,令 $P=1,R(n,2)$。 否则令: $$ t=\left\lfloor\frac{n-1}{2}\right\rfloor-k+1 $$ 构造: $$ P=2,R(n,n-t+1),1,R(n-t,3) $$ 这些排列都只含少量单调段。 逐段比较即可得到所需 LIS, 而直接追踪置换映射可得到恰好 $k$ 个环。 **公共前缀归约。** 同时在 $P,Q$ 前加入: $$ 1,2,\dots,t $$ 并将剩余元素整体增加 $t$。 两个 LIS 长度和连通块数都会增加 $t$。 若 $y=n$,根据 $k<x-1$ 是否成立, 分别加入 $k-1$ 或 $x-2$ 个公共前缀, 归约到 $k=1$ 或 $x=2,y=n$。 若 $x=y$,采用相同的前缀长度, 归约到 $k=1$ 或 $x=y=2$。 若 $1<x<y<n$,当 $k\le x-1$ 时加入 $k-1$ 个, 否则加入 $x-1$ 个,归约到 $k=1$ 或 $x=1$。 每次归约都严格减小规模,并落入已经证明的基形。 这说明必要条件覆盖了所有非边界情形。 每个数只会被写入常数次。 时间复杂度为 $O(n)$,空间复杂度为 $O(n)$。 ## 参考代码 ```cpp #include <bits/stdc++.h> using namespace std; using vi=vector<int>; struct Ans { vi p,q; }; void inc(vi &a,int l,int r) { for(int i=l;i<=r;i++)a.push_back(i); } void dec(vi &a,int r,int l) { for(int i=r;i>=l;i--)a.push_back(i); } Ans one(int n,int x,int y) { vi a,b; if(y<n) { int d=n-y,c=(y-x+1)/2; inc(a,1,x-1); dec(a,n,x); inc(b,2,x); inc(b,x+d,x+d+c-1); b.push_back(1); inc(b,x+d+c,n); dec(b,x+d-1,x+1); } else { int c=(n-x)/2; inc(a,2,x); dec(a,n,x+c+1); a.push_back(1); dec(a,x+c,x+1); inc(b,1,n); } return {a,b}; } vi make_s(int n,int y,int k) { if(k==1) { Ans res=one(n,1,y); reverse(res.q.begin(),res.q.end()); return res.q; } if(y==n-1) { int t=(n+2)/2-k; vi s; s.push_back(t+1); dec(s,n,t+2); dec(s,t,1); return s; } int t=n-y,r=k<=t?k-1:t-1; vi s; inc(s,1,r); vi sub=make_s(n-r,y,k-r); for(auto v:sub)s.push_back(v+r); return s; } Ans low(int n,int y,int k) { vi a,b=make_s(n,y,k); dec(a,n,1); reverse(b.begin(),b.end()); return {a,b}; } Ans two(int n,int k) { vi a,b; a.push_back(1); dec(a,n,2); if(k==n)return {a,a}; b.push_back(2); dec(b,n,n-k+2); b.push_back(1); dec(b,n-k+1,3); return {a,b}; } Ans full(int n,int k) { vi a,b; inc(b,1,n); if(k==(n+2)/2) { a.push_back(1); dec(a,n,2); return {a,b}; } int t=(n-1)/2-k+1; a.push_back(2); dec(a,n,n-t+1); a.push_back(1); dec(a,n-t,3); return {a,b}; } Ans build(int n,int x,int y,int k); Ans pre(int n,int x,int y,int k,int t) { Ans res=build(n-t,x-t,y-t,k-t); vi a,b; inc(a,1,t); inc(b,1,t); for(auto v:res.p)a.push_back(v+t); for(auto v:res.q)b.push_back(v+t); return {a,b}; } Ans build(int n,int x,int y,int k) { if(k==1)return one(n,x,y); if(x==1)return low(n,y,k); if(x==2&&y==2)return two(n,k); if(x==2&&y==n)return full(n,k); if(y==n) { int t=k<x-1?k-1:x-2; return pre(n,x,y,k,t); } if(x==y) { int t=k<x-1?k-1:x-2; return pre(n,x,y,k,t); } int t=k<=x-1?k-1:x-1; return pre(n,x,y,k,t); } void print(const vi &a) { int n=a.size(); for(int i=0;i<n;i++) { if(i)cout<<' '; cout<<a[i]; } cout<<'\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin>>t; while(t--) { int n,x,y,k; cin>>n>>x>>y>>k; bool rev=0; if(x>y) { swap(x,y); rev=1; } bool ok=1; Ans ans; if((x==1||x==n)&&(y==1||y==n)) { if(x==y&&k==n) { if(x==1) { dec(ans.p,n,1); ans.q=ans.p; } else { inc(ans.p,1,n); ans.q=ans.p; } } else if(x==1&&y==n&&k==(n+1)/2) { dec(ans.p,n,1); inc(ans.q,1,n); } else ok=0; } else if(y-x>2*(n-k))ok=0; else ans=build(n,x,y,k); if(!ok) { cout<<"NO"<<'\n'; continue; } if(rev)swap(ans.p,ans.q); cout<<"YES"<<'\n'; print(ans.p); print(ans.q); } return 0; } ```