洛谷P3387 【模板】缩点

题目链接:

kma

题目分析:

缩点之后很明显是个DAG,跑一遍toposort顺便dp
方程\(ans[v] = max(ans[u] + w[v], ans[v])\),ans是到这个连通块的最大点权和,w是这个连通块的点权
注意缩点之后两个连通块之间可能会有很多边,从连通块内不同的点发出,正反分别只能连一次,否则toposort会凉

代码:

#include <bits/stdc++.h>
#define N (100000 + 10)
using namespace std;
inline int read() {
	int cnt = 0, f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
	while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + c - '0'; c = getchar();}
	return cnt * f;
}
int n, m, x, y, to[N], first[N], nxt[N], tot, sta[N], insta[N], w[N], v[N], id[N];
int nxt_[N], first_[N], to_[N], tot_, in[N], low[N], dfn[N], sign, top, cnt;
int ans[N], gmax, fa[N];
int get_father(int x) {return x == fa[x] ? x : fa[x] = get_father(fa[x]);}
inline void Add(int x, int y) {nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
inline void Add_(int x, int y) {nxt_[++tot_] = first_[x], first_[x] = tot_, to_[tot_] = y;}
void tarjan(int u) {
	low[u] = dfn[u] = ++sign, sta[++top] = u, insta[u] = 1;
	for (register int i = first[u]; i; i = nxt[i]) 
		if (!dfn[to[i]]) tarjan(to[i]), low[u] = min (low[u], low[to[i]]);
		else if (insta[to[i]] && dfn[to[i]] < low[u]) low[u] = dfn[to[i]];
	if (low[u] == dfn[u]) {
		++cnt;
		do {w[id[sta[top]] = cnt] += v[sta[top]], insta[sta[top]] = 0;} while(u != sta[top--]);
	}
}
inline void toposort() {
	queue <int> q;
	for (int i = 1; i <= cnt; i++) if (!in[i]) q.push(i), ans[i] = w[i], gmax = max(gmax, ans[i]);
	while (!q.empty()) {
		int u = q.front(); q.pop();
		for (register int i = first_[u]; i; i = nxt_[i]) {
			int v = to_[i]; --in[v];
			if (!in[v]) q.push(v), ans[v] = max(ans[v], ans[u] + w[v]), gmax = max(gmax, ans[v]);
		}
	}
}
int main() {
	n = read(), m = read();
	for (register int i = 1; i <= n; ++i) v[i] = read();
//	for (register int *t = v + 1; t <= v + n + 1; ++t) *t = read();
	for (register int i = 1; i <= m; ++i) x = read(), y = read(), Add(x, y);
	for (register int i = 1; i <= m; ++i) {
		if (!dfn[i]) tarjan(i);
	}
	register int *t = fa + 1; for (;t <= fa + cnt + 1;) {*t = t - fa; ++t;}
	for (register int i = 1; i <= n; ++i) 
		for (register int j = first[i]; j; j = nxt[j]) {
			register int v = to[j];
			if (id[v] != id[i]) {
				register int x = get_father(id[i]), y = get_father(id[v] + cnt);
				if (x == y) continue;
				Add_(id[i], id[v]), ++in[id[v]], fa[y + cnt] = x;
			}
		}
	toposort();
	printf("%d", gmax);
	return 0;
}
posted @ 2019-08-10 10:05  kma_093  阅读(120)  评论(0编辑  收藏  举报