一名苦逼的OIer,想成为ACMer

Iowa_Battleship

BZOJ1123或洛谷3469 [POI2008]BLO-Blockade

BZOJ原题链接

洛谷原题链接

若第\(i\)个点不是割点,那么只有这个点单独形成一个连通块,其它点依旧连通,则答案为\(2\times (n-1)\)
若第\(i\)个点是割点,那么去掉这个点相关的边就会形成\(3\)种构成的连通块:

  1. 由点\(i\)本身构成。
  2. 由点\(i\)的子树(搜索树中)形成若干个连通块。
  3. 由除点\(i\)及其子树的所有其它点构成一个连通块。

于是我们可以在用\(tarjan\)找割点的过程中计算搜索树中每棵子树的大小,并统计答案即可。

#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const int M = 5e5 + 10;
int fi[N], di[M << 1], ne[M << 1], dfn[N], low[N], si[N], l, ti, n;
bool v[N];
ll S[N];
inline int re()
{
	int x = 0;
	char c = getchar();
	bool p = 0;
	for (; c < '0' || c > '9'; c = getchar())
		p |= c == '-';
	for (; c >= '0' && c <= '9'; c = getchar())
		x = x * 10 + (c - '0');
	return p ? -x : x;
}
inline void add(int x, int y)
{
	di[++l] = y;
	ne[l] = fi[x];
	fi[x] = l;
}
inline int minn(int x, int y)
{
	return x < y ? x : y;
}
void tarjan(int x)
{
	int i, y, s = 0, g = 0;
	dfn[x] = low[x] = ++ti;
	si[x] = 1;
	for (i = fi[x]; i; i = ne[i])
	{
		y = di[i];
		if (!dfn[y])
		{
			tarjan(y);
			si[x] += si[y];
			low[x] = minn(low[x], low[y]);
			if (low[y] >= dfn[x])
			{
				g++;
				S[x] += 1LL * si[y] * (n - si[y]);
				s += si[y];
				if (x ^ 1 || g > 1)
					v[x] = 1;
			}
		}
		else
			low[x] = minn(low[x], dfn[y]);
	}
	if (v[x])
		S[x] += 1LL * (n - 1 - s) * (s + 1) + n - 1;
	else
		S[x] = (n - 1) << 1;
}
int main()
{
	int i, x, y, m;
	n = re();
	m = re();
	for (i = 1; i <= m; i++)
	{
		x = re();
		y = re();
		add(x, y);
		add(y, x);
	}
	tarjan(1);
	for (i = 1; i <= n; i++)
		printf("%lld\n", S[i]);
	return 0;
}

posted on 2018-09-10 19:12  Iowa_Battleship  阅读(129)  评论(0编辑  收藏  举报

导航