标签:codeforces 图论
题意:
n(2*10^5)个点m(2*10^5)条边的无向图 要求给无向边定向 使得最后的有向图满足q(2*10^5)个指令 每个指令表示为s->e 即s到e有通路 问 是否有可能
思路:
假设无向图中有圈 那么定向时一定也定成圈 因此想到连通分量概念 容易分析出只有桥的定向才值得讨论 因此可以先对图做边连通分量缩点
由于题中提示图可能不连通 因此得到了森林
我们要解决的就是 根据q个指令(每个指令对应一个线路 树上线路唯一) 将这些线路放在树上 如果某条树边必须保留两个方向则无解 否则有解
暴力的放置线路一定是TLE的 于是想到 对于树上的线段覆盖问题 可以通过树链剖分解决
但是剖分还是比较烦= = 于是我们采用类似线段覆盖的“头加尾减”的方法
定义up[u]表示u节点上面的那条边是向上定向的 同理down[u] 那么如果up[u]&&down[u]则边必须是双向的 现在只需要维护up和down即可 对于每个指令 可以根据s和e的lca分成两部分做“头加尾减” 最后利用树形dp求出点u的up和down 从up和down的定义中可以看出 up[u]=up[son(u)] 同理down
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<string> #include<algorithm> #include<map> #include<set> #include<vector> #include<queue> #include<cstdlib> #include<ctime> #include<cmath> using namespace std; typedef long long LL; #define N 200010 int n, m, q; struct edge { int u, v, flag, next; } ed[N << 1]; int head[N], tot; int dfn[N], low[N], idx, block, sec, stack[N], top; int vis[N], hsh[N], tree[N]; int lca[N][20], dis[N]; int up[N], down[N]; void add(int u, int v) { ed[tot].u = u; ed[tot].v = v; ed[tot].flag = 0; ed[tot].next = head[u]; head[u] = tot++; } void tarjan(int u) { dfn[u] = low[u] = ++idx; stack[++top] = u; tree[u] = sec; for (int i = head[u]; ~i; i = ed[i].next) { int v = ed[i].v; if (ed[i].flag) continue; ed[i].flag = ed[i^1].flag = 1; if (dfn[v] == -1) { tarjan(v); low[u] = min(low[u], low[v]); if (dfn[u] < low[v]) ed[i].flag = ed[i^1].flag = -1; } else low[u] = min(low[u], dfn[v]); } if (dfn[u] == low[u]) { block++; int v; do { v = stack[top--]; hsh[v] = block; } while (u != v); } } void dfs(int u, int c) { hsh[u] = c; for (int i = head[u]; ~i; i = ed[i].next) { if (ed[i].flag == -1) continue; int v = ed[i].v; if (hsh[v] == -1) dfs(v, c); } } void init(int u, int from) { vis[u] = 1; dis[u] = dis[from] + 1; lca[u][0] = from; for (int i = 1; i < 20; i++) { lca[u][i] = lca[lca[u][i - 1]][i - 1]; } for (int i = head[u]; ~i; i = ed[i].next) { int v = ed[i].v; if (v != from) init(v, u); } } int get(int u, int v) { if (dis[v] > dis[u]) swap(u, v); int i, tmp = dis[u] - dis[v]; for (i = 19; tmp; i--) { if (tmp >= (1 << i)) { tmp -= (1 << i); u = lca[u][i]; } } if (u == v) return u; for (i = 19; i >= 0; i--) { if (lca[u][i] != lca[v][i]) { u = lca[u][i]; v = lca[v][i]; } } return lca[u][0]; } bool ok(int u) { vis[u] = 0; for (int i = head[u]; ~i; i = ed[i].next) { int v = ed[i].v; if (vis[v]) { if (!ok(v)) return false; up[u] += up[v]; down[u] += down[v]; } } return !(up[u] && down[u]); } int main() { memset(head, -1, sizeof (head)); scanf("%d%d%d", &n, &m, &q); for (int i = 1; i <= m; i++) { int u, v; scanf("%d%d", &u, &v); add(u, v); add(v, u); } memset(dfn, -1, sizeof (dfn)); for (int i = 1; i <= n; i++) { if (dfn[i] == -1) { sec++; tarjan(i); } } int tmp = tot; tot = 0; memset(head, -1, sizeof (head)); for (int i = 0; i < tmp; i++) { if (ed[i].flag == -1 && hsh[ed[i].u] != hsh[ed[i].v]) { add(hsh[ed[i].u], hsh[ed[i].v]); } } n = block; for (int i = 1; i <= n; i++) { if (!vis[i]) init(i, i); } bool ans = true; for (int i = 1; i <= q; i++) { int u, v; scanf("%d%d", &u, &v); if (tree[u] != tree[v]) { puts("No"); return 0; } u = hsh[u]; v = hsh[v]; if (u != v) { int fa = get(u, v); up[u]++; up[fa]--; down[v]++; down[fa]--; } } for (int i = 1; i <= n; i++) { if (vis[i]) { ans = ok(i); if (!ans) break; } } if (ans) puts("Yes"); else puts("No"); return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
CodeForces 555E Case of Computer Network
标签:codeforces 图论
原文地址:http://blog.csdn.net/houserabbit/article/details/46832323