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;
}