[CQOI2013]图的逆变换

来源:互联网 发布:平台介绍淘宝代理开店 编辑:程序博客网 时间:2024/06/03 18:18

题意

给一个n结点m条边的有向图D,可以这样构造图E:给D的每条边u->v,在E中建立一个点uv,然后对于D中的两条边u->v和v->w,在E中从uv向vw连一条有向边。E中不含有其他点和边。
输入E,你的任务是判断是否存在相应的D。注意,D可以有重边和自环。

测试数据个数T10
D的边数(即E的点数)m300
Time Limits:2000ms
Memory Limits:512000KB

分析

OutuEu点出边指向的点的集合。我们会有一个结论:若存在D,u,v Outu=Outv or OutuOutv=ϕ
结论必要性显然,充分性引用题解里的证明:可以尝试构造出一个可能的图 G。对于每一个 u,找到的若干 x, y的导出子图是一个完全二分图,对左部在图 G 中对应的边到达的点和右部在图 G 中对应的边出发的点规定为 G 中一个全新的节点即可。
这样我们可以用并查集,对于点u,将Outu里的点并一起,并记录并查集的大小。最后扫一遍,看每个点Out集合大小是否跟并查集大小相等。

代码

#include <cstdio>#include <cstring>using namespace std;const int N = 310,M = 1e6;int g[N],next[M],to[M],d[N],f[N],size[N];int tot,n,k;void add(int x,int y) {    to[++ tot] = y;    next[tot] = g[x];    g[x] = tot;    d[x] ++;}int get(int x) {    if (f[x] != x) f[x] = get(f[x]);    return f[x];}int main() {    int T;    scanf("%d",&T);    while (T --) {        memset(g,0,sizeof(g));        memset(d,0,sizeof(d));        tot = 0;        scanf("%d%d",&n,&k);        for (int i = 0;i < n;i ++) f[i] = i,size[i] = 1;        for (int i = 1;i <= k;i ++) {            int x,y;            scanf("%d%d",&x,&y);            add(x,y);        }        for (int i = 0;i < n;i ++) if (g[i]) {            int j,last = to[g[i]];            int y = get(last);            for (j = next[g[i]];j;j = next[j]) {                int x = get(to[j]);                if (x == y) continue;                f[x] = y;                size[y] += size[x];            }        }        int flag = 1;        for (int i = 0;i < n;i ++) if (g[i]) {            int j = get(to[g[i]]);            if (size[j] != d[i]) {                flag = 0;                break;            }        }        if (flag) printf("Yes\n");        else printf("No\n");    }}
0 0