题目链接
题目大意
有一张 n 个点的无向图,小 B 每次会询问某两个点之
间是否有边相连,小 A 每次回答 yes 或 no.
如果在小 B 把所有
n(n−1)2
条边问完之前,小 B 就能
确定这整张图是否联通,小 A 就输了.
现在让你当小 A,依次对每个询问回答 yes 或 no,求
一种获胜方案.
4≤n≤1500.
思路
对于一个联通块而言,如果其中还存在没有被询问过的边,小B可以把它们放到最后问,而这些边不问就能知道这个联通块是联通的(废话),这样小B不用问完所有边就能赢,因此小A只要时刻保证图中任意一个联通块中不存在没有被询问过的边,小A就能赢,因此这个题是一定有必胜方案的。
那么对于每个询问过的边,我们可以将它们标记为YES边和NO边,分别代表这个边存在、不存在。如果标记某条边
(u,v)
为YES后,可以联通两个联通块,并且如果标记
(u,v)
为NO后,就能确定两个联通块
A、B
之间不联通(即
∀a∈A,b∈B,不存在(a,b)
),那么
(u,v)
必须标记为YES(因为这时标记为NO的话,小B就能确定整个图不联通),其他情况下,
(u,v)
必须标记为NO(这样才能让小B尽量晚点确定这两个联通块是否联通)
#include "game.h"
#define MAXV 2020
int n;
int f[MAXV],size[MAXV]; //带size标记的并查集
int num[MAXV][MAXV]; //num[a][b]=联通块a、b之间的NO边的个数
void initialize(int N)
{
n=N;
for(int i=0;i<=n;i++)
{
f[i]=i;
size[i]=1;
}
}
int findSet(int x)
{
if(f[x]==x) return x;
return f[x]=findSet(f[x]);
}
int hasEdge(int u,int v)
{
u++;
v++;
int rootu=findSet(u),rootv=findSet(v);
if(rootu==rootv) return 0; //u和v在同一个联通块内,就说u与v之间没边
if(num[rootu][rootv]+1==size[rootu]*size[rootv]) //(u,v)是NO边的话,两个联通块之间就能判定为不联通,那么(u,v)一定要回答YES
{
for(int i=1;i<=n;i++)
{
num[rootu][i]+=num[rootv][i];
num[i][rootu]+=num[i][rootv];
}
f[f[rootv]]=f[rootu];
size[rootu]+=size[rootv];
return 1;
}
else //其他情况回答NO
{
num[rootu][rootv]++;
num[rootv][rootu]++;
}
return 0;
}