[UOJ 26][IOI 2014]Game(构造题)

题目链接

http://uoj.ac/problem/26

题目大意

有一张 n 个点的无向图,小 B 每次会询问某两个点之
间是否有边相连,小 A 每次回答 yes 或 no.
如果在小 B 把所有 n(n1)2 条边问完之前,小 B 就能
确定这整张图是否联通,小 A 就输了.
现在让你当小 A,依次对每个询问回答 yes 或 no,求
一种获胜方案. 4n1500.

思路

对于一个联通块而言,如果其中还存在没有被询问过的边,小B可以把它们放到最后问,而这些边不问就能知道这个联通块是联通的(废话),这样小B不用问完所有边就能赢,因此小A只要时刻保证图中任意一个联通块中不存在没有被询问过的边,小A就能赢,因此这个题是一定有必胜方案的。
那么对于每个询问过的边,我们可以将它们标记为YES边和NO边,分别代表这个边存在、不存在。如果标记某条边 (u,v) 为YES后,可以联通两个联通块,并且如果标记 (u,v) 为NO后,就能确定两个联通块 AB 之间不联通(即 aAbB,(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;
}

你可能感兴趣的:(图)