Description
给出一张二分图,左边点编号为1~n,右边为n+1到2n{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≤10000n\leq10000n≤10000
总的连边/询问次数不得超过2∗1062*10^62∗106
Solution
显然的可以发现,对弈每次询问,我们可以把右边的点分成几个联通块,
为了让右边查询出的联通块和左边我们连出的联通块对应,我只能用过联通块中点的个数来区分,
那么题目就变成了:
我们构造集合集SiS_iSi,使得集合之间没有交集,所有集合的并集为全集,且集合的大小两两不同,
同样的方法构造集合集TiT_iTi,
构造的两个集合集需要满足,对于任意a,满足存在Si∩Tj={a}S_i\cap T_j=\{a\}Si∩Tj={a}(交集只有这一个)
显然的,当n满足存在m使得n=(m+1)m2n=\frac{(m+1)m}{2}n=2(m+1)m,一定有解,
以n=10为例:
S1={1}S_1=\{1\}S1={1}
S2={2,3}S_2=\{2,3\}S2={2,3}
S3={4,5,6}S_3=\{4,5,6\}S3={4,5,6}
S4={7,8,9,10}S_4=\{7,8,9,10\}S4={7,8,9,10}
T1={10}T_1=\{10\}T1={10}
T2={9,6}T_2=\{9,6\}T2={9,6}
T3={8,5,3}T_3=\{8,5,3\}T3={8,5,3}
T4={7,4,2,1}T_4=\{7,4,2,1\}T4={7,4,2,1}
这样既可以出唯一解了,
那么对于不满足条件的n怎么办呢?
也可以用同样的方法,先不考虑联通块大小相同所带来的无法一一对应问题,
具体流程是:我们先按上面的方法分好S集,使得相邻SiS_iSi集合大小差不超过1,
以n=8为例:
S1={1}S_1=\{1\}S1={1}
S2={2,3}S_2=\{2,3\}S2={2,3}
S3={4,5}S_3=\{4,5\}S3={4,5}
S4={6,7,8}S_4=\{6,7,8\}S4={6,7,8}
接下来,集合T的构造对于所有i满足∣Si∣=∣Ti∣|S_i|=|T_i|∣Si∣=∣Ti∣,
我们从T的最后一个开始构造,假设TiT_iTi需要放入a个元素,那么我们就从S中,可选元素个数最大的前a个中各挑出一个来组成TiT_iTi
T1={1}T_1=\{1\}T1={1}
T2={6,2}T_2=\{6,2\}T2={6,2}
T3={7,4}T_3=\{7,4\}T3={7,4}
T4={8,5,3}T_4=\{8,5,3\}T4={8,5,3}
加上个表帮助理解
当前T的选择 | 当前S的剩余 |
---|---|
S1{1},S2{2,3},S3{4,5},S4{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} | |
T4{8,5,3}T_4\{8,5,3\}T4{8,5,3} | S1{1},S2{2},S3{4},S4{6,7}S_1\{1\},S_2\{2\},S_3\{4\},S_4\{6,7\}S1{1},S2{2},S3{4},S4{6,7} |
T3{7,4}T_3\{7,4\}T3{7,4} | S1{1},S2{2},S3{},S4{6}S_1\{1\},S_2\{2\},S_3\{\},S_4\{6\}S1{1},S2{2},S3{},S4{6} |
T2{6,2}T_2\{6,2\}T2{6,2} | S1{1},S2{},S3{},S4{}S_1\{1\},S_2\{\},S_3\{\},S_4\{\}S1{1},S2{},S3{},S4{} |
T1{1}T_1\{1\}T1{1} | S1{},S2{},S3{},S4{}S_1\{\},S_2\{\},S_3\{\},S_4\{\}S1{},S2{},S3{},S4{} |
可以归纳证明,对于任意n,这种构造方法均可行,
接下来我们再来解决联通块大小相同所带来的无法一一对应问题,
显然的,当大小为1的集合出现两个时很好解决,可以直接特判掉;
设两个大小相同的集合分别为Sx,SyS_x,S_ySx,Sy,按照定义必然∣Tx∣=∣Ty∣|T_x|=|T_y|∣Tx∣=∣Ty∣
我们发现,第一轮过后,我们已经知道点1的出边是什么,记为a1a_1a1
第二轮时,我们再集合TxT_xTx中加入点1,那么右边与a1a_1a1相联通的联通块就一定是对应集合TxT_xTx的,
这样我们就成功区分出了Tx,TyT_x,T_yTx,Ty所对应的联通块,
对于Sx,SyS_x,S_ySx,Sy,可以发现:这两个S集合与两T集合的两两交集大小均为1,
既然我们可以通过点1区分出Tx,TyT_x,T_yTx,Ty,那么我们在构造时就使得Tx,SyT_x,S_yTx,Sy的交集为空,即让TxT_xTx少一个元素,
那么TxT_xTx对应的右边联通块,与SxS_xSx对应的右边联通块,交集大小为1,
那么TxT_xTx对应的右边联通块,与SyS_ySy对应的右边联通块,交集大小为空,
这样就完美解决了一一对应的问题,
剩下的就是简单的集合求并,
(注意:因为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;
}