Description
给出一张二分图,左边点编号为1~n,右边为
n
+
1
到
2
n
{n+1}到{2n}
n+1到2n,
这张图满足以下特性:每个点的度数都恰好是1(左边每个点连出去一条,右边每个点被连一条,边为双向)
也就是这张图的连边方案可以用一个长度排列为n的排列描述(每个左边连向右边哪一个),
现在你只知道这张图有2n个点却不知道连边方案,但你可以通过以下流程询问交互库:
第一轮:
阶段1. addedge(x,y)
:交互库会在左边两点x,y之间连一条无向边;本函数可以执行多次
阶段2. query(x,y)
:交互库会回答右边两点x,y之间的连通性;本函数可以执行多次
注意:在阶段1是不能执行query(x,y)
函数,阶段2不能执行 addedge(x,y)
函数
也就是一次性连完,一次性问完
第二轮:(图已被恢复至原二分图)
阶段1. addedge(x,y)
:交互库会在左边两点x,y之间连一条无向边;本函数可以执行多次
阶段2. query(x,y)
:交互库会回答右边两点x,y之间的连通性;本函数可以执行多次
注意:同样在阶段1是不能执行query(x,y)
函数,阶段2不能执行 addedge(x,y)
函数
你必须在第二轮结束后输出答案,
n
≤
10000
n\leq10000
n≤10000
总的连边/询问次数不得超过
2
∗
1
0
6
2*10^6
2∗106
Solution
显然的可以发现,对弈每次询问,我们可以把右边的点分成几个联通块,
为了让右边查询出的联通块和左边我们连出的联通块对应,我只能用过联通块中点的个数来区分,
那么题目就变成了:
我们构造集合集
S
i
S_i
Si,使得集合之间没有交集,所有集合的并集为全集,且集合的大小两两不同,
同样的方法构造集合集
T
i
T_i
Ti,
构造的两个集合集需要满足,对于任意a,满足存在
S
i
∩
T
j
=
{
a
}
S_i\cap T_j=\{a\}
Si∩Tj={a}(交集只有这一个)
显然的,当n满足存在m使得
n
=
(
m
+
1
)
m
2
n=\frac{(m+1)m}{2}
n=2(m+1)m,一定有解,
以n=10为例:
S
1
=
{
1
}
S_1=\{1\}
S1={1}
S
2
=
{
2
,
3
}
S_2=\{2,3\}
S2={2,3}
S
3
=
{
4
,
5
,
6
}
S_3=\{4,5,6\}
S3={4,5,6}
S
4
=
{
7
,
8
,
9
,
10
}
S_4=\{7,8,9,10\}
S4={7,8,9,10}
T
1
=
{
10
}
T_1=\{10\}
T1={10}
T
2
=
{
9
,
6
}
T_2=\{9,6\}
T2={9,6}
T
3
=
{
8
,
5
,
3
}
T_3=\{8,5,3\}
T3={8,5,3}
T
4
=
{
7
,
4
,
2
,
1
}
T_4=\{7,4,2,1\}
T4={7,4,2,1}
这样既可以出唯一解了,
那么对于不满足条件的n怎么办呢?
也可以用同样的方法,先不考虑联通块大小相同所带来的无法一一对应问题,
具体流程是:我们先按上面的方法分好S集,使得相邻
S
i
S_i
Si集合大小差不超过1,
以n=8为例:
S
1
=
{
1
}
S_1=\{1\}
S1={1}
S
2
=
{
2
,
3
}
S_2=\{2,3\}
S2={2,3}
S
3
=
{
4
,
5
}
S_3=\{4,5\}
S3={4,5}
S
4
=
{
6
,
7
,
8
}
S_4=\{6,7,8\}
S4={6,7,8}
接下来,集合T的构造对于所有i满足
∣
S
i
∣
=
∣
T
i
∣
|S_i|=|T_i|
∣Si∣=∣Ti∣,
我们从T的最后一个开始构造,假设
T
i
T_i
Ti需要放入a个元素,那么我们就从S中,可选元素个数最大的前a个中各挑出一个来组成
T
i
T_i
Ti
T
1
=
{
1
}
T_1=\{1\}
T1={1}
T
2
=
{
6
,
2
}
T_2=\{6,2\}
T2={6,2}
T
3
=
{
7
,
4
}
T_3=\{7,4\}
T3={7,4}
T
4
=
{
8
,
5
,
3
}
T_4=\{8,5,3\}
T4={8,5,3}
加上个表帮助理解
当前T的选择 | 当前S的剩余 |
---|---|
S 1 { 1 } , S 2 { 2 , 3 } , S 3 { 4 , 5 } , S 4 { 6 , 7 , 8 } S_1\{1\},S_2\{2,3\},S_3\{4,5\},S_4\{6,7,8\} S1{1},S2{2,3},S3{4,5},S4{6,7,8} | |
T 4 { 8 , 5 , 3 } T_4\{8,5,3\} T4{8,5,3} | S 1 { 1 } , S 2 { 2 } , S 3 { 4 } , S 4 { 6 , 7 } S_1\{1\},S_2\{2\},S_3\{4\},S_4\{6,7\} S1{1},S2{2},S3{4},S4{6,7} |
T 3 { 7 , 4 } T_3\{7,4\} T3{7,4} | S 1 { 1 } , S 2 { 2 } , S 3 { } , S 4 { 6 } S_1\{1\},S_2\{2\},S_3\{\},S_4\{6\} S1{1},S2{2},S3{},S4{6} |
T 2 { 6 , 2 } T_2\{6,2\} T2{6,2} | S 1 { 1 } , S 2 { } , S 3 { } , S 4 { } S_1\{1\},S_2\{\},S_3\{\},S_4\{\} S1{1},S2{},S3{},S4{} |
T 1 { 1 } T_1\{1\} T1{1} | S 1 { } , S 2 { } , S 3 { } , S 4 { } S_1\{\},S_2\{\},S_3\{\},S_4\{\} S1{},S2{},S3{},S4{} |
可以归纳证明,对于任意n,这种构造方法均可行,
接下来我们再来解决联通块大小相同所带来的无法一一对应问题,
显然的,当大小为1的集合出现两个时很好解决,可以直接特判掉;
设两个大小相同的集合分别为
S
x
,
S
y
S_x,S_y
Sx,Sy,按照定义必然
∣
T
x
∣
=
∣
T
y
∣
|T_x|=|T_y|
∣Tx∣=∣Ty∣
我们发现,第一轮过后,我们已经知道点1的出边是什么,记为
a
1
a_1
a1
第二轮时,我们再集合
T
x
T_x
Tx中加入点1,那么右边与
a
1
a_1
a1相联通的联通块就一定是对应集合
T
x
T_x
Tx的,
这样我们就成功区分出了
T
x
,
T
y
T_x,T_y
Tx,Ty所对应的联通块,
对于
S
x
,
S
y
S_x,S_y
Sx,Sy,可以发现:这两个S集合与两T集合的两两交集大小均为1,
既然我们可以通过点1区分出
T
x
,
T
y
T_x,T_y
Tx,Ty,那么我们在构造时就使得
T
x
,
S
y
T_x,S_y
Tx,Sy的交集为空,即让
T
x
T_x
Tx少一个元素,
那么
T
x
T_x
Tx对应的右边联通块,与
S
x
S_x
Sx对应的右边联通块,交集大小为1,
那么
T
x
T_x
Tx对应的右边联通块,与
S
y
S_y
Sy对应的右边联通块,交集大小为空,
这样就完美解决了一一对应的问题,
剩下的就是简单的集合求并,
(注意:因为T有一个位置少了一个元素,所以有一个元素无法通过集合合并求出,要通过排除法求出)
Code
#include "worldline.h"
#include <cstdio>
#include <algorithm>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define efo(i,q) for(int i=A[q];i;i=B[i][0])
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
using namespace std;
const int N=100500;
int read(int &n)
{
char ch=' ';int q=0,w=1;
for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
if(ch=='-')w=-1,ch=getchar();
for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
void WA()
{
printf("WAWAWAWAWAWAWAWAWAWAWAWA\n");
printf("WAWAWAWAWAWAWAWAWAWAWAWA\n");
printf("WAWAWAWAWAWAWAWAWAWAWAWA\n");
printf("WAWAWAWAWAWAWAWAWAWAWAWA\n");
printf("WAWAWAWAWAWAWAWAWAWAWAWA\n");
printf("WAWAWAWAWAWAWAWAWAWAWAWA\n");
while(1);
}
int n;
int g[N],g1[N];
int bz[200][N],bz1[200][N];
int b[200][N];
int b0;
int z[N],TI;
int d[N],p[N];
void Update()
{
fod(i,p[0]-1,1)if(b[p[i]][0]>b[p[i+1]][0])
{
for(int j=i;j<p[0]&&b[p[j]][0]>b[p[j+1]][0];++j)swap(p[j],p[j+1]);
}
}
int Find(int q,int w)
{
int i,j;
for(i=j=1;i<=bz[q][0]&&j<=bz1[w][0];)
{
if(bz[q][i]==bz1[w][j])return bz[q][i];
if(bz[q][i]<bz1[w][j])++i;
else ++j;
}
return -1;
}
void doit0(int *anse)
{
int q,w,e=0;
fod(i,b0,1)
{
w=b[b0][b[b0][0]--];
g1[w]=i;
fod(j,b0-1,b0-i+1)addedge(b[j][b[j][0]],w),g1[b[j][b[j][0]--]]=i;
}
next_step();///
++TI;
fo(i,1,n)if(z[i]<TI)
{
z[i]=TI;
d[d[0]=1]=i;
fo(j,i+1,n)if(z[j]<TI)if(query(i,j))
{
z[j]=TI;
d[++d[0]]=j;
}
bz1[d[0]][0]=d[0];
fo(j,1,d[0])bz1[d[0]][j]=d[j];
}
fo(i,1,n)
{
anse[i]=Find(g[i],g1[i]);
}
}
void doit1(int *anse)
{
int q,w,e=0;
fod(i,b0,1)
{
w=b[b0][b[b0][0]--];
g1[w]=i;
fod(j,b0-1,b0-i+1)addedge(b[j][b[j][0]],w),g1[b[j][b[j][0]--]]=i;
}
next_step();///
++TI;
fo(i,1,n)if(z[i]<TI)
{
z[i]=TI;
d[d[0]=1]=i;
fo(j,i+1,n)if(z[j]<TI)if(query(i,j))
{
z[j]=TI;
d[++d[0]]=j;
}
if(!bz1[d[0]][0])
{
bz1[d[0]][0]=d[0];
fo(j,1,d[0])bz1[d[0]][j]=d[j];
}else
{
bz1[0][0]=d[0];
fo(j,1,d[0])bz1[0][j]=d[j];
}
}
if(bz1[1][1]==bz[0][1])swap(bz1[1][1],bz1[0][1]);
else if(bz1[0][1]==bz[1][1])swap(bz[1][1],bz[0][1]);
else if(bz1[1][1]==bz[1][1])swap(bz[1][1],bz[0][1]),swap(bz1[1][1],bz1[0][1]);
fo(i,1,n)
{
anse[i]=Find(g[i],g1[i]);
}
}
int query_permutation(int n,int anse[])
{
::n=n;
int q,w;
if (n==2) return 0;
new_round();
fo(i,0,n+10)g[i]=g1[i]=d[i]=p[i]=0;b0=0;
for(w=q=1;w<=n&&w+q-1<=n;w+=q,++q)
{
g[w]=++b0;
fo(i,1,q-1)g[i+w]=b0,addedge(i+w,w);
}
fo(i,0,b0+10)fo(j,0,n+10)b[i][j]=bz[i][j]=bz1[i][j]=0;
int LA=n-w+1;
if(LA)
{
fo(i,w+1,n)addedge(w,i);
}
fo(i,1,n)
{
b[g[i]][++b[g[i]][0]]=i;
}
next_step();///
++TI;
fo(i,1,n)if(z[i]<TI)
{
z[i]=TI;
d[d[0]=1]=i;
fo(j,i+1,n)if(z[j]<TI&&query(i,j))
{
z[j]=TI;
d[++d[0]]=j;
}
if(!bz[d[0]][0])
{
bz[d[0]][0]=d[0];
fo(j,1,d[0])bz[d[0]][j]=d[j];
}else
{
if(d[0]!=LA||bz[0][0])WA();
bz[0][0]=d[0];
fo(j,1,d[0])bz[0][j]=d[j];
}
}
new_round();///
if(LA==1)
{
doit1(anse);
return 1;
}
if(!LA)
{
doit0(anse);
return 1;
}
fo(i,1,b0-1)p[i]=i+1;p[0]=b0-1;
if(LA)
{
++p[0];
fod(i,b0-1,1)if(b[p[i]][0]>b[0][0])p[i+1]=p[i];
else {p[i+1]=0;break;}
}
fo(i,0,p[0])d[i]=b[p[i]][0];d[p[0]+1]=-1;
q=0;
fo(i,1,n)g1[i]=-1;
fod(i,d[0],1)
{
if(d[i]==LA&&d[i]==d[i+1])
{
if(p[p[0]])
{
w=b[p[p[0]]][b[p[p[0]]][0]--];
addedge(1,w);
g1[w]=0;
fod(j,p[0]-1,p[0]-d[i]+1)if(p[j])addedge(b[p[j]][b[p[j]][0]],w),g1[b[p[j]][b[p[j]][0]--]]=0;
}else
{
w=b[p[p[0]-1]][b[p[p[0]-1]][0]--];
addedge(1,w);
g1[w]=0;
fod(j,p[0]-2,p[0]-d[i]+1)addedge(b[p[j]][b[p[j]][0]],w),g1[b[p[j]][b[p[j]][0]--]]=0;
}
}else
{
w=b[p[p[0]]][b[p[p[0]]][0]--];
g1[w]=d[i];
fod(j,p[0]-1,p[0]-d[i]+1)addedge(b[p[j]][b[p[j]][0]],w),g1[b[p[j]][b[p[j]][0]--]]=d[i];
}
Update();
}
next_step();///
++TI;
z[bz[1][1]]=TI;
bz1[0][0]=0;
fo(i,1,n)if(z[i]<TI&&query(i,bz[1][1]))
{
z[i]=TI;
bz1[0][++bz1[0][0]]=i;
}
fo(i,1,n)if(z[i]<TI)
{
z[i]=TI;
d[d[0]=1]=i;
fo(j,i+1,n)if(z[j]<TI)if(query(i,j))
{
z[j]=TI;
d[++d[0]]=j;
}
if(!bz1[d[0]][0])
{
bz1[d[0]][0]=d[0];
fo(j,1,d[0])bz1[d[0]][j]=d[j];
}else WA();
}
anse[1]=bz[1][1];
if(LA)
{
if(Find(0,0)!=-1)
{
fo(i,1,LA)swap(bz[0][i],bz[LA][i]);
}
}
++TI;
z[anse[1]]=TI;
fo(i,2,n)if(g1[i]>-1)
{
q=anse[i]=Find(g[i],g1[i]);
z[anse[i]]=TI;
}
fo(i,2,n)if(g1[i]==-1)
{
fo(j,1,n)if(z[j]<TI)anse[i]=j;
}
return 1;
}