【Codeforces 1172D】Nauuo and Portals
构造题
题目大意:
给定 $n\times n$ 的图,并给定 $2n$ 个限制。
定义左上角为 $(0,0)$。
前 $n$ 个限制 $p_1,p_2,\dots,p_n$ 表示从 $(i,0)$ 出发的人一直往右走,要走到 $(p_i,n+1)$。
后 $n$ 个限制 $q_1,q_2,\dots,q_n$ 表示从 $(0,i)$ 出发的人一直往下走,要走到 $(n+1,q_i)$。
保证 $p,q$ 为 $1\sim n$ 的一个排列。
现在可以放置若干对传送门,每对传送门是两个坐标 $(x_1,y_1),(x_2,y_2)$,表示一个人走到 $(x_1,y_1)$ 后会瞬移到 $(x_2,y_2)$,走到 $(x_2,y_2)$ 后会瞬移到 $(x_1,y_1)$,即双向通行。并且通过传送门不改变行走方向。必须满足 $1\leq x_1,x_2,y_1,y_2\leq n$ 且同一个位置最多一个传送门。
要求输出一组合法的安放传送门的方案,使得所有 $2n$ 个限制均满足。
题解:
考虑把 $n\times n$ 的问题转化为 $(n-1)\times(n-1)$ 的问题。
以最开始为例,我们考虑要走到 $(1,n+1)$ 的人所在行 $p$ 和要走到 $(n+1,1)$ 的人所在列 $q$。
那么我们如图所示,建造传送门 $(p,1),(1,n)$ 以及 $(1,q),(n,1)$。
那么,行 $p$ 和列 $q$ 的人显然能够走到它的合法位置,而原来在第一行的人将会到第 $n$ 行,第 $n$ 行的人将会到第 $p$ 行。同理在第一列的人将会到第 $n$ 列,第 $n$ 列的人将会到第 $q$ 列。
再考虑一些特殊情况,如下图。
表示有一个人是从第 $n$ 行走到第一行(或者第 $n$ 列走到第一列,同理)。那么只需要开一对传送门即可。
这种情况就无需开传送门了。
以上,我们花费了 $0\sim 2$ 扇传送门,使得最终到第一行和第一列的人满足了条件,并令第 $2\sim n$ 行和 $2\sim n$ 列都保持每行/列有且仅有一个人。那么我们就转化为了一个 $(n-1)\times (n-1)$ 的子问题。继续按照上述方法讨论即可。到 $1\times 1$ 的时候停止。
时间复杂度 $O(n)$。
Code:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1005;
int n,R[N],C[N],top,iR[N],iC[N];
struct doors{
int r1,c1,r2,c2;
}d[N<<1];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i)cin>>R[i],iR[R[i]]=i;
for(int i=1;i<=n;++i)cin>>C[i],iC[C[i]]=i;
for(int i=1;i<n;++i){
if(R[i]==i&&C[i]==i)continue;
if(iR[i]==n&&iC[i]==n){
d[++top]=(doors){i,n,n,i};
R[n]=R[i],C[n]=C[i];
iR[R[n]]=n;iC[C[n]]=n;
continue;
}
if(iC[i]==n){
d[++top]=(doors){iR[i],i,i,n};
C[n]=C[i];iC[C[n]]=n;
R[iR[i]]=R[i],iR[R[iR[i]]]=iR[i];
continue;
}
if(iR[i]==n){
d[++top]=(doors){i,iC[i],n,i};
R[n]=R[i];iR[R[n]]=n;
C[iC[i]]=C[i],iC[C[iC[i]]]=iC[i];
continue;
}
d[++top]=(doors){iR[i],i,i,n},
d[++top]=(doors){i,iC[i],n,i};
int r=iR[i],c=iC[i];
R[r]=R[n],R[n]=R[i];
C[c]=C[n],C[n]=C[i];
iR[R[r]]=r,iC[C[c]]=c;
iR[R[n]]=n,iC[C[n]]=n;
}
cout<<top<<'\n';
for(int i=1;i<=top;++i)
cout<<d[i].r1<<' '<<d[i].c1<<' '<<d[i].r2<<' '<<d[i].c2<<'\n';
return 0;
}