[POI2008]BLO-Blockade

题面

[POI2008]BLO-Blockade

题解

深度优先遍历这幅图。
s i z [ x ] siz[x] siz[x] 表示在搜索树中,以 x x x 为根的子树的大小。
注意不连通的关系是双向的,所以 ( x , y ) , ( y , x ) (x,y),(y,x) (x,y),(y,x)算两次。
对于当前点 x x x,有两种情况:

  • x x x 是割点
    那么删去 x x x 之后会有如下的点不连通:
    • x x x 在搜索树上的子树中所有的节点与其它的点:
      ∑ k = 1 t s i z [ k ] ∗ ( n − s i z [ k ] ) ( t 为 子 节 点 总 数 ) \sum_{k = 1}^{t}siz[k] * (n - siz[k])(t为子节点总数) k=1tsiz[k](nsiz[k])t
    • x x x 与其余的 n − 1 n - 1 n1 个节点。
    • x x x 的祖先节点和 x x x 的祖先节点的兄弟姐妹节点,与 x x x以及它的子树:
      ( n − 1 − ∑ k = 1 t s i z [ k ] ) × ( 1 + ∑ k = 1 t s i z [ k ] ) (n - 1 -\sum_{k = 1}^{t}siz[k]) \times(1 + \sum_{k = 1}^{t}siz[k] ) (n1k=1tsiz[k])×(1+k=1tsiz[k])
  • x x x 不是割点
    那么删去与 x x x 相连的边之后不会影响除 x x x 以外的节点的连通性。所以只有 x x x 与其它节点不连通。
    a n s = 2 × ( n − 1 ) ans = 2 \times (n - 1) ans=2×(n1)
    于是我们在 t a r j a n tarjan tarjan 求割点的同时求出以上的值,就能得到答案了。

代码如下

#include<cstdio>
#include<iostream>
using std::min;
const int N = 1e5 + 5,M = 5e5 + 5;
struct edge {
	int next,to;
}a[M << 1];
int head[N],n,m,a_size = 1,root;
inline void add(int u,int v) {
	a[++a_size] = (edge){head[u],v};
	head[u] = a_size;
	a[++a_size] = (edge){head[v],u};
	head[v] = a_size;
}
int dfn[N],low[N],siz[N],num = 0;
long long ans[N]; bool cut[N];
void tarjan(int x) {
	dfn[x] = low[x] = ++num;
	int flag = 0,sum = 0; siz[x] = 1;
	for(int i = head[x]; i; i = a[i].next) {
		int y = a[i].to;
		if(!dfn[y]) {
			tarjan(y); siz[x] += siz[y];
			low[x] = min(low[x],low[y]);
			if(low[y] >= dfn[x]) {
				flag++; sum += siz[y]; 
				ans[x] += 1ll * siz[y] * (n - siz[y]);
				if(x != root || flag > 1) cut[x] = true;
			}
		}
		else low[x] = min(low[x],dfn[y]);
	}
	if(cut[x]) ans[x] += 1ll * (n - 1 - sum) * (1 + sum) + n - 1;
	else ans[x] = (n - 1) << 1;
}
inline int read() {
	int x = 0,flag = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')flag = -1;ch = getchar();}
	while(ch >='0' && ch <='9'){x = (x << 3) + (x << 1) + ch - 48;ch = getchar();}
	return x * flag;
}
int main() {
	n = read(),m = read();
	for(int i = 1; i <= m; i++) {
		int u = read(),v = read();
		add(u,v);
	}
	for(int i = 1; i <= n; i++) {
		if(dfn[i]) continue;
		root = i; tarjan(root);
	}
	for(int i = 1; i <= n; i++) printf("%lld\n",ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值