2017多校九 05题 hdu 6165 FFF at Valentine 缩点 dp找最长链/拓扑排序
2017-08-23 19:53
489 查看
题目链接
题意
判定一个图是不是单向连通图。// 其实就是poj 2186,不过poj的那道题数据水了些= =
// 浏览题目时看成了FFT at Valentine吓死我= =
思路
先套路一发,tarjan求强联通分量,缩点,至此预处理完成。(这部分详细内容烦请移步本菜另一篇 强联通分量 缩点 tarjan 入门题小集)然后怎么处理呢?
法一
现在我们得到了一个DAG,直观想法就是有没有一个长链串起了图中所有的点。于是可以模仿一下树形dp的做法,dfs一遍,dep[u]=max{dep[v]+1|∀v,u→v}.Code
#include <cstdio> #include <cstring> #define maxn 1010 #define maxm 6010 #include <stack> #define inf 0x3f3f3f3f using namespace std; stack<int> s; int dfn[maxn], low[maxn], ne[maxn], ne2[maxn], belong[maxn], ind[maxn], tot, tot2, cnt, scc, in[maxn], kas, dep[maxn], vis[maxn]; bool ok; struct Edge { int from, to, ne; Edge(int a = 0, int b = 0, int c = 0) : from(a), to(b), ne(c) {} }edge[maxm], edge2[maxm]; void add(int u, int v) { edge[tot] = Edge(u, v, ne[u]); ne[u] = tot++; } void add2(int u, int v) { edge2[tot2] = Edge(u, v, ne2[u]); ne2[u] = tot2++; } void init() { tot = 0; memset(ne, -1, sizeof(ne)); } void init2() { tot2 = 0; ok = false; memset(vis, 0, sizeof(vis)); memset(ne2, -1, sizeof(ne2)); memset(dep, 0, sizeof(dep)); } void tarjanInit() { cnt = scc = 0; while (!s.empty()) s.pop(); memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(ind, 0, sizeof(ind)); memset(in, 0, sizeof(in)); } void tarjan(int u) { dfn[u] = low[u] = ++cnt; in[u] = true; s.push(u); for (int i = ne[u]; i != -1; i = edge[i].ne) { int v = edge[i].to; if (!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (in[v]) low[u] = min(low[u], dfn[v]); } if (low[u] == dfn[u]) { ++scc; while (true) { int v = s.top(); in[v] = false; s.pop(); belong[v] = scc; if (v == u) break; } } } void contract() { init2(); for (int i = 0; i < tot; ++i) { int u = edge[i].from, v = edge[i].to; if (belong[u] == belong[v]) continue; add2(belong[u], belong[v]); ++ind[belong[v]]; } } void dfs(int u) { vis[u] = true; dep[u] = 1; for (int i = ne2[u]; i != -1; i = edge2[i].ne) { int v = edge2[i].to; if (!vis[v]) dfs(v); dep[u] = max(dep[u], dep[v] + 1); } } void work() { int n, m; init(); tarjanInit(); scanf("%d%d", &n, &m); for (int i = 0; i < m; ++i) { int u, v; scanf("%d%d", &u, &v); add(u, v); } for (int i = 1; i <= n; ++i) { if (!dfn[i]) tarjan(i); } contract(); for (int i = 1; i <= scc; ++i) { if (!ind[i]) { dfs(i); if (dep[i] == scc) ok = true; break; } } if (ok) printf("I love you my love and our love save us!\n"); else printf("Light my fire!\n"); } int main() { int T; scanf("%d", &T); while (T--) work(); return 0; }
法二
在新图中,必定只能有一个入度为 0 的点,否则走了一个,另一个就没办法达到了。删去这个点和它指向的边,图中剩下的部分中也必定只能有一个入度为 0 的点,道理同上。
依次类推。
咦?这不就是拓扑排序吗!
Code
#include <cstdio> #include <cstring> #define maxn 1010 #define maxm 6010 #include <stack> #include <queue> #define inf 0x3f3f3f3f using namespace std; stack<int> s; queue<int> q; int dfn[maxn], low[maxn], ne[maxn], ne2[maxn], belong[maxn], ind[maxn], tot, tot2, cnt, scc, in[maxn], kas, dep[maxn], vis[maxn]; struct Edge { int from, to, ne; Edge(int a = 0, int b = 0, int c = 0) : from(a), to(b), ne(c) {} }edge[maxm], edge2[maxm]; void add(int u, int v) { edge[tot] = Edge(u, v, ne[u]); ne[u] = tot++; } void add2(int u, int v) { edge2[tot2] = Edge(u, v, ne2[u]); ne2[u] = tot2++; } void init() { tot = 0; memset(ne, -1, sizeof(ne)); } void init2() { tot2 = 0; memset(vis, 0, sizeof(vis)); memset(ne2, -1, sizeof(ne2)); memset(dep, 0, sizeof(dep)); } void tarjanInit() { cnt = scc = 0; while (!s.empty()) s.pop(); memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(ind, 0, sizeof(ind)); memset(in, 0, sizeof(in)); } void tarjan(int u) { dfn[u] = low[u] = ++cnt; in[u] = true; s.push(u); for (int i = ne[u]; i != -1; i = edge[i].ne) { int v = edge[i].to; if (!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (in[v]) low[u] = min(low[u], dfn[v]); } if (low[u] == dfn[u]) { ++scc; while (true) { int v = s.top(); in[v] = false; s.pop(); belong[v] = scc; if (v == u) break; } } } void contract() { init2(); for (int i = 0; i < tot; ++i) { int u = edge[i].from, v = edge[i].to; if (belong[u] == belong[v]) continue; add2(belong[u], belong[v]); ++ind[belong[v]]; } } bool topsort() { int zero = 0, idx; for (int i = 1; i <= scc; ++i) { if (ind[i] == 0) ++zero, idx = i; } if (zero > 1) return false; while (!q.empty()) q.pop(); q.push(idx); while (!q.empty()) { int u = q.front(); q.pop(); zero = 0; for (int i = ne2[u]; i != -1; i = edge2[i].ne) { int v = edge2[i].to; --ind[v]; if (ind[v] == 0) ++zero, idx = v; } if (zero > 1) return false; if (zero == 1) q.push(idx); } return true; } void work() { int n, m; init(); tarjanInit(); scanf("%d%d", &n, &m); for (int i = 0; i < m; ++i) { int u, v; scanf("%d%d", &u, &v); add(u, v); } for (int i = 1; i <= n; ++i) { if (!dfn[i]) tarjan(i); } contract(); if (topsort()) printf("I love you my love and our love save us!\n"); else printf("Light my fire!\n"); } int main() { int T; scanf("%d", &T); while (T--) work(); return 0; }
相关文章推荐
- HDU 6165 FFF at Valentine(tarjan缩点+拓扑排序)
- hdu 6165 FFF at Valentine (Tarjan算法,scc+dp)
- HDU 6165 FFF at Valentine(强连通缩点)
- HDU 6165 FFF at Valentine (tarjan缩点+拓扑判任意两点联通)
- HDU --- 6165 FFF at Valentine 多校第九场 【强联通缩点 + 维护拓扑序】
- hdu 6165 FFF at Valentine(tarjan缩点+拓扑排序)
- 2017 Multi-University Training Contest - Team 9 1005&&HDU 6165 FFF at Valentine【强联通缩点+拓扑排序】
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- HDU 6165 FFF at Valentine
- hdu--6165--FFF at Valentine
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- 【多校训练】hdu 6165 FFF at Valentine
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- HDU 6165 FFF at Valentine(强连通分量+拓扑排序)
- HDU - 6165 FFF at Valentine
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)