缩点

P3387 【模板】缩点

缩点,即把一张有向有环图中的 \(\rm SCC\) 都缩成一个个点,形成一个 \(\rm DAG\)

对于本题,若一个点被选到了,则该点所在的 \(\rm SCC\) 中的所有点都可以选到,那当然都要选了。

老师:天上掉馅饼,我不选!

杠精:我偏不选!

所以,我们可以直接把一个 \(\rm SCC\) 缩成一个点,这个点的点权 \(sum(i)\) 为原 \(\rm SCC\) 内所有点的点权和。

首先是找 \(\rm SCC\),直接用 \(\rm Tarjan\) 即可。有向图的强连通分量

找到之后就将每个 \(\rm SCC\) 缩成一个点,建新图;然后使用 \(\rm dp\),对于新图上的边 \(<u,v>\)\(dp(v)\gets\max(dp(v),dp(u)+sum(v))\)

注意到 \(\rm dp\) 是无后效性的,所以我们还需要进行一次拓扑排序。


\(\rm Code\)

#include <iostream>
#include <cstdio>
#include <stack>
#include <queue>
#define re register
using namespace std;

inline int read()
{
	re int x = 0, f = 0;
	re char c = getchar();
	while (c < '0' || c > '9')
	{
		f |= c == '-';
		c = getchar();
	}
	while (c >= '0' && c <= '9')
	{
		x = (x << 3) + (x << 1) + (c ^ '0');
		c = getchar();
	}
	return f ? -x : x;
}

inline void write(int x)
{
	if (x < 0)
	{
		putchar('-');
		x = -x;
	}
	if (x > 9)
	{
		write(x / 10);
	}
	putchar(x % 10 ^ '0');
}

inline int max2(int x, int y)
{
	return x > y ? x : y;
}

inline int min2(int x, int y)
{
	return x < y ? x : y;
}
//-----------------------------------------------------------
const int MAXN = 1e4 + 5;
const int MAXM = 1e5 + 5;

int n, m, cnt1, cnt2, Time;
int w[MAXN], head1[MAXN], head2[MAXN], dfn[MAXN], low[MAXN], c[MAXN], sum[MAXN], in[MAXN], dp[MAXN];
bool ins[MAXN];
stack<int> sta;
queue<int> q;

struct edge
{
	int to, nxt;
}e1[MAXM], e2[MAXM];

void add1(int u, int v)
{
	e1[++cnt1] = edge{v, head1[u]};
	head1[u] = cnt1;
}

void add2(int u, int v)
{
	e2[++cnt2] = edge{v, head2[u]};
	head2[u] = cnt2;
	in[v]++;
}

void tarjan(int u)
{
	dfn[u] = low[u] = ++Time;
	sta.push(u);
	ins[u] = true;
	for (re int i = head1[u]; i; i = e1[i].nxt)
	{
		int v = e1[i].to;
		if (!dfn[v])
		{
			tarjan(v);
			low[u] = min2(low[u], low[v]);
		}
		else if (ins[v])
		{
			low[u] = min2(low[u], dfn[v]);
		}
	}
	if (dfn[u] == low[u])
	{
		int v = 0;
		while (u != v)
		{
			v = sta.top();
			sta.pop();
			ins[v] = false;
			c[v] = u;
			sum[u] += w[v];
		}
	}
}

void topo()
{
	queue<int> q;
	for (re int i = 1; i <= n; i++)
	{
		if (c[i] == i && !in[i]) //为SCC的起点
		{
			q.push(i);
			dp[i] = sum[i];
		}
	}
	while (!q.empty())
	{
		int u = q.front();
		q.pop();
		for (re int i = head2[u]; i; i = e2[i].nxt)
		{
			int v = e2[i].to;
			dp[v] = max2(dp[v], dp[u] + sum[v]);
			if (!--in[v])
			{
				q.push(v);
			}
		}
	}
}

int main()
{
	n = read(), m = read();
	for (re int i = 1; i <= n; i++)	
	{
		w[i] = read();
	}
	for (re int i = 1; i <= m; i++)
	{
		int u = read(), v = read();
		add1(u, v);
	}
	for (re int i = 1; i <= n; i++)
	{
		if (!dfn[i])
		{
			tarjan(i);
		}
	}
	for (re int u = 1; u <= n; u++)
	{
		for (int i = head1[u]; i; i = e1[i].nxt)
		{
			int v = e1[i].to;
			if (c[u] != c[v]) //不在同一个SCC内,建边
			{
				add2(c[u], c[v]);
			}
		}
	}
	topo();
	int ans = 0;
	for (int i = 1; i <= n; i++)
	{
		ans = max2(ans, dp[i]);
	}
	write(ans);
	return 0;
}
posted @ 2021-08-07 18:10  mango09  阅读(167)  评论(0编辑  收藏  举报
-->