CTS2023 琪露诺的符卡交换

First Post:

Last Update:

Word Count:
676

Read Time:
3 min

Page View: loading...

题意

给定 矩阵 ,包含 个,求方案交换若干位置使得每个位置只交换一次,且交换后每行为 的排列。

思路

因为要求行是个排列,所以每行内部的顺序是可以任意调换的。

不妨调换行内的顺序,使得每列为一个 的排列,注意这是不需要交换操作的(我们先假设这可以做到)。

然后交换 ,这样每行就为一个 的排列。


证明第一步一定有解,我们每次需要在 行中找到 的排列,假设存在 行,其中值的种类数 满足 ,那么无解(Hall 定理不成立),假定还剩下 列(并且每种值都有 个),根据鸽巢原理一定存在某种值的数量大于 ,矛盾。

CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510,FSIZE=1<<26,INF=0x7f7f7f7f;
int tn,n,cur[N],a[N],sze,d[N],w[N],p0[N],p1[N],is[N][N],c[N][N];
struct{int n,t,v;}b[N*N];
char BuF[FSIZE],*InF=BuF;
template<typename T>void read(T &x){
for(;48>*InF||*InF>57;++InF);
for(x=0;47<*InF&&*InF<58;x=x*10+(*InF++^48));
}
void _add(int x,int y,int z){
b[sze++]={a[x],y,z};
a[x]=sze-1;
}
void add(int x,int y,int z){
_add(x,y,z);
_add(y,x,0);
}
bool bfs(){
memset(w,127,sizeof(w));
for(int h=w[0]=0,t=d[0]=0;h<=t;++h)
for(int i=cur[d[h]]=a[d[h]];~i;i=b[i].n)
if(b[i].v&&w[b[i].t]>w[d[h]]+1)
w[d[++t]=b[i].t]=w[d[h]]+1;
return(w[n+n+1]<INF);
}
int dfs(int x,int flow){
if(x==n+n+1) return(flow);
int used=0,aflow;
for(int &i=cur[x];~i;i=b[i].n)
if(w[b[i].t]==w[x]+1&&b[i].v&&(aflow=dfs(b[i].t,min(b[i].v,flow)))){
b[i].v-=aflow;
b[i^1].v+=aflow;
if((used+=aflow)==flow) break;
}
return(used);
}
int dinic(){
int re=0;
for(;bfs();re+=dfs(0,INF));
return(re);
}
void work(){
memset(a,-1,sizeof(a));
sze=0;
read(n);
for(int i=1;i<=n;++i){
add(0,i,0);
p0[i]=sze-2;
add(i+n,n+n+1,0);
p1[i]=sze-2;
for(int j=1,x;j<=n;++j){
read(x);
add(i,x+n,1);
c[i][j]=sze-2;
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
b[p0[j]].v=1;
b[p0[j]^1].v=0;
b[p1[j]].v=1;
b[p1[j]^1].v=0;
}
dinic();
for(int j=1;j<=n;++j)
for(int k=1;k<=n;++k)
if(b[c[j][k]^1].v){
is[j][i]=k;
b[c[j][k]^1].v=0;
}
}
printf("%d\n",n*(n-1)>>1);
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
printf("%d %d %d %d\n",i,is[i][j],j,is[j][i]);
}
int main(){
fread(BuF,1,FSIZE,stdin);
for(read(tn);tn--;work());
return(0);
}