[Luogu] P3469 [POI2008]BLO-Blockade

Description

给定一张无向图,求每个点被封锁之后有多少个有序点对\((x,y)(x!=y,1\le{x,y}\le{n})\)满足\(x\)无法到达\(y\)

Solution

注意到如果一个点不是割点,那么它的答案肯定是\(2(n-1)\)(无序点对)

而如果它是割点,那么封锁它后,即把这个点抠出来后,原来的图一定被分成了若干部分。对于每个部分,设它的节点个数为\(t\),那么它对答案造成的贡献为\(t*(n-t)\),因为这\(t\)个点到其他任意\(n-t\)个点都到不了。

现在我们来思考如何算出这些部分的节点个数。其实很简单。首先节点\(x\)自身构成一个大小为\(1\)的部分。然后对于每个满足\(dfn[x]\le{low[y]}\)的节点\(y\),大小是\(sz[y]\)。最后可能还有一个部分,由除了上述节点之外的所有节点构成,大小为\(n-1-\sum_{y=1}^tsz[y]\)

注意要开\(long\ long\)

Code

#include <bits/stdc++.h>

using namespace std;

#define ll long long

int n, m, tot, ind, rt, sz[100005], cut[100005], hd[100005], to[1000005], nxt[1000005], low[100005], dfn[100005];

ll res[100005];

int read()
{
	int x = 0, fl = 1; char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * fl;
}

void add(int u, int v)
{
	tot ++ ;
	to[tot] = v;
	nxt[tot] = hd[u];
	hd[u] = tot;
	return;
}

void Tarjan(int x)
{
	low[x] = dfn[x] = ++ ind;
	int t = 0, sum = 0;
	sz[x] = 1;
	for (int i = hd[x]; i; i = nxt[i])
	{
		int y = to[i];
		if (!dfn[y])
		{
			Tarjan(y);
			sz[x] += sz[y];
			low[x] = min(low[x], low[y]);
			if (low[y] >= dfn[x])
			{
				t ++ ;
				if (x != rt || t > 1) cut[x] = 1;
				if (cut[x]) res[x] += 1ll * sz[y] * (n - sz[y]), sum += sz[y];
			}
		}
		else low[x] = min(low[x], dfn[y]);
	}
	if (cut[x]) res[x] += 1ll * (n - 1) + 1ll * (n - 1 - sum) * (sum + 1);
	else res[x] = 2ll * (n - 1);
	return;
}

int main()
{
	n = read(); m = read();
	for (int i = 1; i <= m; ++ i)
	{
		int u = read(), v = read();
		add(u, v); add(v, u);
	}
	rt = 1, Tarjan(1);
	for (int i = 1; i <= n; i ++ )
		printf("%lld\n", res[i]);
	return 0;
}
posted @ 2020-11-02 13:50  andysj  阅读(38)  评论(0编辑  收藏  举报