题意
给一个n结点m条边的有向图D,可以这样构造图E:给D的每条边u->v,在E中建立一个点uv,然后对于D中的两条边u->v和v->w,在E中从uv向vw连一条有向边。E中不含有其他点和边。
输入E,你的任务是判断是否存在相应的D。注意,D可以有重边和自环。
测试数据个数
T≤10
D
的边数(即
Time
Limits:2000ms
Memory
Limits:512000KB
分析
设
Outu
为
E
中
结论必要性显然,充分性引用题解里的证明:可以尝试构造出一个可能的图 G。对于每一个 u,找到的若干 x, y的导出子图是一个完全二分图,对左部在图 G 中对应的边到达的点和右部在图 G 中对应的边出发的点规定为 G 中一个全新的节点即可。
这样我们可以用并查集,对于点
u
,将
代码
#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");
}
}