题解 luogu P3469 【[POI2008]BLO-Blockade】

  • 既然刚学了割边割点,我就来写一波题解。。
  1. 根据割点的定义,若节点 i i i不是割点,就把节点i关联的所有边都去掉后,只有与其他 n − 1 n - 1 n1个节点之间是联通的。但是因为题目中 ( x , y ) (x,y) (x,y) ( y , x ) (y,x) (y,x)算不同的点对,所以答案为 2 ∗ ( n − 1 ) 2*(n - 1) 2(n1)
  2. 若节点 i i i是割点,则把节点 i i i关联的边去掉后,图会分成若干个连通块。我们需要求出连通块的大小,两两相乘再相加。设在搜索树上,节点 i i i的子节点集合中,有 t t t个点 s 1 , s 2 , s 3 , . . . , s t s_{1},s_{2},s_{3},...,s_{t} s1,s2,s3,...,st满足割点的判定法则 d f n [ i ] ≤ l o w [ s k ] dfn[i]\leq low[s_{k}] dfn[i]low[sk]。最后会变成至多 t + 2 t + 2 t+2个连通块。
  3. 我们可以在tarjan算法深搜的时候,顺便求出每棵子树的大小。若 s i z e [ x ] size[x] size[x]为以x为根的子树大小。最后的答案很明显就是
    ∑ k = 1 t s i z e [ s k ] ∗ ( n − s i z e [ s k ] ] ) + 1 ∗ ( n − 1 ) + ( n − 1 − ∑ k = 1 t s i z e [ s k ] ) ∗ ( 1 + ∑ k = 1 t s i z e [ s k ] ) \sum_{k = 1}^{t}size[s_{k}]*(n - size[s_{k}]])+1*(n-1)+(n-1-\sum_{k = 1}^{t}size[s_{k}])*(1+\sum_{k = 1}^{t}size[s_{k}]) k=1tsize[sk](nsize[sk]])+1(n1)+(n1k=1tsize[sk])(1+k=1tsize[sk])
    所以最后的代码就是

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 1e5 + 10, M = 5e5 + 10;
struct edge {
	int y, nxt;
}e[M * 2];
int lin[M], len = 1;
int dfn[N], low[N], num, size[N];
int n, m;
ll ans[N];
bool cut[N];

inline ll read() {
	ll s = 0, f = 1;
	char ch;
	for(; ch < '0' || ch > '9'; ch = getchar())	if(ch == '-')	f = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar())	s = (s << 1) + (s << 3) + ch -'0';
	return s * f;
}

inline void insert(int xx, int yy) {
	e[++len] = {yy, lin[xx]};
	lin[xx] = len;
	e[++len] = {xx, lin[yy]};
	lin[yy] = len;
}

inline void tarjan(int x) {
	dfn[x] = low[x] = ++num;
	size[x] = 1;
	int flag = 0, sum = 0;
	for(int i = lin[x]; i; i = e[i].nxt) {
		int y = e[i].y;
		if(!dfn[y]) {
			tarjan(y);
			size[x] += size[y];
			low[x] = min(low[x], low[y]);
			if(low[y] >= dfn[x]) {
				flag++;
				ans[x] += (ll)size[y] * (n - size[y]);
				sum += size[y];
				if(x != 1 || flag > 1)	cut[x] = 1;
			}
		} else low[x] = min(low[x], dfn[y]);
	}
	if(cut[x])	ans[x] += (ll)(n - sum - 1) * (sum + 1) + (n - 1);
	else	ans[x] = 2 * (n - 1);
}

int main() {
	n = read();
	m = read();
	for(int i = 1; i <= m; ++i)	{
		int xx = read(), yy = read();
		if(xx == yy)	continue;
		insert(xx, yy);
	}
	tarjan(1);
	for(int i = 1; i <= n; ++i)	printf("%lld\n", ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值