城镇封锁
题意:如题
思路:使用Tarjan算法找出割点,割点为连通图中,去掉该点就会使图不再连通。
对于非割点,其贡献为该点到其它所有点即2 * (n - 1)
对于割点,则为其每棵子树上的点数*其它所有点+所有子树包括该点上的点数*其它所有点,由于此时该点与其它点的贡献只被记了一次,所以要加上n - 1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+3;
int n,m,cnt,root;
vector<int>vec[N];
int dfn[N],low[N],sz[N];
ll ans[N];
void tarjan(int u){
dfn[u]=low[u]=++cnt;
sz[u]=1;
bool st=0;
int child=0,sum=0;
for(int v:vec[u]){
if(dfn[v]){
low[u]=min(low[u],dfn[v]);
continue;
}
tarjan(v);
sz[u]+=sz[v];
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
child++;
sum+=sz[v];
if(u!=root || child>1) st=1;
ans[u]+=1ll*sz[v]*(n-sz[v]); //统计这段子树与非子树节点(包含本身)有多少贡献
}
}
if(!st) ans[u]=2*(n-1); //不是割点则贡献只有该点到所有其它点
else ans[u]+=1ll*(n-1-sum)*(sum+1)+n-1; //最后加上所有子树节点与非子树节点匹配的贡献和,因为各点与该点已经匹配过一遍,所以需要加上n-1
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
vec[a].emplace_back(b);
vec[b].emplace_back(a);
}
for(int i=1;i<=n;i++)
if(!dfn[i]){
root=i;
tarjan(i);
}
for(int i=1;i<=n;i++) cout<<ans[i]<<'\n';
// system("pause");
}