BZOJ1123: [POI2008]BLO
Description
Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 所有towns连通。
Input
输入n<=100000 m<=500000及m条边
Output
输出n个数,代表如果把第i个点去掉,将有多少对点不能互通。
Sample Input
5 5
1 2
2 3
1 3
3 4
4 5
1 2
2 3
1 3
3 4
4 5
Sample Output
8
8
16
14
8
8
16
14
8
题解Here!
首先可以对任意一个节点$i$进行讨论:
1. 假如$i$不是割点,那么这个图只有$i$是独立在外面的。
由于求的是有序点对,所以除了$i$以外的$n-1$个点作为一个大的连通图对$i$加边,即为$2\times(n-1)$对。
2. 假如$i$是割点,那么会把图分为$x$个连通块以及$i$本身。
由于$Tarjan$在求割点的过程中是一棵搜索树往下遍历,所以除了它和它的子树外,还会有其他剩余点共同构成另一个连通块。
设$i$所有子树的和为$w$,第$i$个子树的节点总数为$size[i]$,点对的数量便为:
$$\sum_{i=1}^xsize[i]\times(n-size[x])+(n-1)+(1+w)\times(n-w-1)$$
所以在求割点的过程中每遇到一个$low[v]>=dfn[u]$便把对数加上$size[i]\times(n-size[i])$。
最后假如不是割点那直接把对数更新为$2\times(n-1)$是割点则加上$n-1+(n-w-1)\times(w+1)$。
最后遍历输出答案即可。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #define MAXN 100010 using namespace std; int n,m,c=1,d=1; int head[MAXN],deep[MAXN],low[MAXN],size[MAXN]; bool cut[MAXN]; long long ans[MAXN]; struct Edge{ int next,to; }a[MAXN*10]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } inline void add_edge(int x,int y){ a[c].to=y;a[c].next=head[x];head[x]=c++; a[c].to=x;a[c].next=head[y];head[y]=c++; } void dfs(int x){ int s=0,w=0; deep[x]=low[x]=d++; size[x]=1; for(int i=head[x];i;i=a[i].next){ int v=a[i].to; if(!deep[v]){ dfs(v); low[x]=min(low[x],low[v]); size[x]+=size[v]; if(low[v]>=deep[x]){ ans[x]+=1LL*size[v]*(n-size[v]); w+=size[v]; s++; if(x!=1||s>=2)cut[x]=true; } } else low[x]=min(low[x],deep[v]); } if(!cut[x])ans[x]=2*(n-1); else ans[x]+=1LL*(n-w-1)*(w+1)+n-1; } void work(){ int x,y; n=read();m=read(); for(int i=1;i<=m;i++){ x=read();y=read(); add_edge(x,y); } dfs(1); for(int i=1;i<=n;i++)printf("%lld\n",ans[i]); } int main(){ work(); return 0; }