您的位置:首页 > 其它

[CQOI2013]图的逆变换

2015-08-24 16:50 239 查看

题意

给一个n结点m条边的有向图D,可以这样构造图E:给D的每条边u->v,在E中建立一个点uv,然后对于D中的两条边u->v和v->w,在E中从uv向vw连一条有向边。E中不含有其他点和边。

输入E,你的任务是判断是否存在相应的D。注意,D可以有重边和自环。

测试数据个数T≤10T \le 10

DD的边数(即EE的点数)m≤300m \le 300

TimeTime Limits:2000msLimits:2000ms

MemoryMemory Limits:512000KBLimits:512000KB

分析

设OutuOut_u为EE中uu点出边指向的点的集合。我们会有一个结论:若存在DD,∀u,v\forall u,v Outu=Outv Out_u =Out_v or Outu∩Outv=ϕOut_u \cap Out_v = \phi

结论必要性显然,充分性引用题解里的证明:可以尝试构造出一个可能的图 G。对于每一个 u,找到的若干 x, y的导出子图是一个完全二分图,对左部在图 G 中对应的边到达的点和右部在图 G 中对应的边出发的点规定为 G 中一个全新的节点即可。

这样我们可以用并查集,对于点uu,将OutuOut_u里的点并一起,并记录并查集的大小。最后扫一遍,看每个点OutOut集合大小是否跟并查集大小相等。

代码

#include <cstdio>
#include <cstring>
using namespace std;

const int N = 310,M = 1e6;
int g
,next[M],to[M],d
,f
,size
;
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");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: