P3469

[POI2008]BLO-Blockade

题面翻译

B 城有 \(n\) 个城镇,\(m\) 条双向道路。

每条道路连结两个不同的城镇,没有重复的道路,所有城镇连通。

把城镇看作节点,把道路看作边,容易发现,整个城市构成了一个无向图。

请你对于每个节点 \(i\) 求出,把与节点 \(i\) 关联的所有边去掉以后(不去掉节点 \(i\) 本身),无向图有多少个有序点 \((x,y)\),满足 \(x\)\(y\) 不连通。

【输入格式】

第一行包含两个整数 \(n\)\(m\)

接下来 \(m\) 行,每行包含两个整数 \(a\)\(b\),表示城镇 \(a\)\(b\) 之间存在一条道路。

【输出格式】

输出共 \(n\) 行,每行输出一个整数。

\(i\) 行输出的整数表示把与节点 \(i\) 关联的所有边去掉以后(不去掉节点 \(i\) 本身),无向图有多少个有序点 \((x,y)\),满足 \(x\)\(y\) 不连通。

【数据范围】

\(n\le 100000\)\(m\le500000\)

样例 #1

样例输入 #1

5 5
1 2
2 3
1 3
3 4
4 5

样例输出 #1

8
8
16
14
8

这其实是树形DP中很常见的做法

但太久没做树了

其实没有必要区分是不是割点 我们关心的是 x与子树的哪些点连的边 去掉后会破坏连通性

首先 对于每个x 删掉边后肯定为孤立点 所以有 ans[x]+=n-1

接下来 仿照树形DP中 分为孤立点集之间的情况 和 集内与集外的情况

怎么计算呢?

sum:当前统计到的 x的子树内的孤立点个数

由于对称性 我们不用考虑 v这个孤立集与之后遍历到的孤立集之间的贡献 只用考虑与v之前遍历到的 反正最后答案×2 v->vafter 与 vafter->v是一样的

对于前者 由判断割点的第一个方法 :low[v]>=dfn[x] 更深入的理解就是删掉x后 v孤立了 也就是说 删掉 x与v的边后 v与其它子树内的点就不连通了 所以 每次遇到 low[v]>=dfn[x]的点 更新low[x] (tarjan基操) 然后 ans[x]+=sum×siz[v] 然后 sum+=siz[v]

对于后者 在最后统计完sum后 ans[x]+=sum*(n-sum-1)(孤立点集内×外)即可

注意!!! sum的统计一定是严格在 low[v]>=dfn[x]的情况下的 因为非孤立点之间对答案没有贡献

最后 由于是有序点对 记得×2

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e5+5;
int n,m,ans[N];
struct Graph {
	int from,nxt,to;
} edge[N<<1];
int head[N],cnt;
inline void add(int u,int v) {
	cnt++;
	edge[cnt].to=v;
	edge[cnt].from=u;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
int low[N],dfn[N],stack_[N],pt,times,cut[N];
int siz[N];
void tarjan(int x) {
	low[x]=dfn[x]=++times;
	siz[x]=1;
	int sum=0;
	for(int i=head[x]; i; i=edge[i].nxt) {
		int v=edge[i].to;
		if(!dfn[v]) {
			tarjan(v);
			siz[x]+=siz[v];
			low[x]=min(low[x],low[v]);
			if(low[v]>=dfn[x]) {
				ans[x]+=sum*siz[v];
				sum+=siz[v];
			}
		} else low[x]=min(low[x],dfn[v]);
	}
	ans[x]+=sum*(n-sum-1);
}
signed main() {
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1; i<=m; i++) {
		int u,v;
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	tarjan(1);
	for(int i=1; i<=n; i++)
		cout<<(ans[i]+n-1)*2<<"\n";
	return 0;
}
posted @ 2023-04-24 17:56  N0zoM1z0  阅读(15)  评论(0编辑  收藏  举报