标签:c++ max ack ems 判断 queue 关系 min 选择
题意和思路看这篇博客就行了:https://www.cnblogs.com/cjyyb/p/10507937.html
有个问题需要注意:对于每个scc,只需要考虑进入这个scc的时间即可,其实和从哪个点进没有关系,因为scc内每个点都可以互相到达,所以只需记录时间就囊括了所有的情况,比如时间3从1号点进和时间4从2号点进是等价的,这也是为什么可以随便选择一颗生成树的原因。对于scc的出边,边的长度是val[u] + val[v] - 1,因为假设从scc x的根 到点scc y的点v,时间是val[u] + 1,而scc y的根到v的距离是val[v],所以要求原装态需要减去这个。当然,这个边需要对两个scc的gcd的gcd取模,以判断从这条边到下一条边可以到达的状态。
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 100010; int head[maxn], Next[maxn * 2], ver[maxn * 2], tot; int heade[maxn], Nexte[maxn * 2 * 50], edgee[maxn * 2 * 50], vere[maxn * 2 * 50], tote, cnt; int n, m, D, GCD, g[maxn], f[maxn][51], pre[maxn][51], deg[maxn], val[maxn], c[maxn]; int dfn[maxn], low[maxn], num, top, Stack[maxn]; char v[maxn][51]; bool vis[maxn], ins[maxn]; vector<int> scc[maxn]; void add(int x, int y) { ver[++tot] = y;Next[tot] = head[x];head[x] = tot; } void adde(int x, int y, int z) { vere[++tote] = y, edgee[tote] = z, Nexte[tote] = heade[x], heade[x] = tote, deg[y]++; } void solve(int x, int p) { val[x] = p, vis[x] = 1; for (int i = head[x]; i; i = Next[i]) { int y = ver[i]; if(c[y] != cnt) continue; if(!vis[y]) solve(y, p + 1); else GCD = __gcd(GCD, abs(p + 1 - val[y])); } } void tarjan(int x) { dfn[x] = low[x] = ++num; Stack[++top] = x, ins[x] = 1; for (int i = head[x]; i; i = Next[i]) { if (!dfn[ver[i]]) { tarjan(ver[i]); low[x] = min(low[x], low[ver[i]]); } else if (ins[ver[i]]) low[x] = min(low[x], dfn[ver[i]]); } if (dfn[x] == low[x]) { cnt++; int y; do { y = Stack[top--], ins[y] = 0; c[y] = cnt, scc[cnt].push_back(y); } while(x != y); GCD = D, solve(x, 0); g[cnt] = GCD; for (int i = 0; i < GCD; i++) for (int j = 0; j < scc[cnt].size(); j++) { for (int k = i; k < D; k += GCD) if(v[scc[cnt][j]][k] == ‘1‘) { v[scc[cnt][j]][i] = ‘1‘; break; } } for (int i = 0; i < GCD; i++) for (int j = 0; j < scc[cnt].size(); j++) { if(v[scc[cnt][j]][(val[scc[cnt][j]] + i) % GCD] == ‘1‘) pre[cnt][i]++; } } } void dp(void) { int ans = 0; memset(f, -0x3f, sizeof(f)); f[c[1]][0] = pre[c[1]][0]; queue<int> q; for (int i = 1; i <= cnt; i++) { if (!deg[i]) q.push(i); } while(!q.empty()) { int x = q.front(); q.pop(); for (int i = heade[x]; i; i = Nexte[i]) { int y = vere[i]; deg[y]--; if(deg[y] == 0) q.push(y); } for (int i = heade[x]; i; i = Nexte[i]) { for (int j = 0; j < D; j++) { int y = vere[i], z = edgee[i]; f[y][(z + j) % D] = max(f[y][(z + j) % D], f[x][j] + pre[y][(z + j) % g[y]]); } } for (int i = 0; i < D; i++) ans = max(ans, f[x][i]); } printf("%d\n", ans); } int main() { int x, y; scanf("%d%d%d", &n, &m, &D); for (int i = 1; i <= m; i++) { scanf("%d%d", &x, &y); add(x, y); } for (int i = 1; i <= n; i++) scanf("%s", v[i]); for (int i = 1; i <= n; i++) { if(!dfn[i]) tarjan(i); } for (int j = 1; j <= n; j++) for (int i = head[j]; i; i = Next[i]) { int y = ver[i]; if(c[j] == c[y]) continue; int dd = __gcd(g[c[j]], g[c[y]]); int d = (val[j] - val[y] + 1 + D) % D; for (int k = d % dd; k < D; k += dd) { adde(c[j], c[y], k); } } dp(); }
Codeforces 1137C Museums Tour (强连通分量, DP)
标签:c++ max ack ems 判断 queue 关系 min 选择
原文地址:https://www.cnblogs.com/pkgunboat/p/10562413.html