码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces 1137C Museums Tour (强连通分量, DP)

时间:2019-03-20 00:40:36      阅读:193      评论:0      收藏:0      [点我收藏+]

标签: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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!