[BZOJ1123]:[POI2008]BLO(塔尖)
题目传送门
题目描述
Byteotia城市有n个towns、m条双向roads。
每条road连接两个不同的towns,没有重复的road。
所有towns连通。
输入格式
输入n,m及m条边。
输出格式
输出n个数,代表如果把第i个点去掉,将有多少对点不能互通。
样例
样例输入:
5 5
1 2
2 3
1 3
3 4
4 5
样例输出:
8
8
16
14
8
题解
看到这道题,应该想到缩点。
首先,如果一个点不是割点,那么把它去掉,不会影响与它无关的点对,那么,去掉它之后,减少的点对的个数即为2n-2。
如果这个点是割点,那么去掉它,减少的点对个数不止2n-2,还需要将所有与他它通的联通块的大小两两相乘再相加。
考虑塔尖,在深度优先遍历的同时就能够完成统计答案的工作。
那么,删除一个割点后,减少的点对数量即为:
$ size[ s_{1} ]×(n-size[ s_{1} ])+size[ s_{2} ]×(n-size[ s_{2} ])+...+size[ s_{t} ]×(n-size[ s_{t} ])+n-1+(n-1- \sum \limits_{k=1}^{t} size[ s_{k} ])×(1+ \sum \limits_{k=1}^{t} size[ s_{k} ])$
代码时刻
#include<bits/stdc++.h> using namespace std; struct rec { int nxt; int to; }e[1000001]; int head[100001],cnt; int n,m; int dfn[100001],low[100001],size[100001],tot; long long ans[100001]; bool cut[100001]; void add(int x,int y) { e[++cnt].nxt=head[x]; e[cnt].to=y; head[x]=cnt; } void tarjan(int x) { dfn[x]=low[x]=++tot; int flag=0,sum=0; size[x]=1; for(int i=head[x];i;i=e[i].nxt) { if(!dfn[e[i].to]) { tarjan(e[i].to); size[x]+=size[e[i].to]; low[x]=min(low[x],low[e[i].to]); if(low[e[i].to]>=dfn[x]) { flag++; sum+=size[e[i].to];//用来最后的∑ ans[x]+=1LL*size[e[i].to]*(n-size[e[i].to]);//先统计进去 if(x!=1||flag>1)cut[x]=1; } } else low[x]=min(low[x],dfn[e[i].to]); } if(cut[x])ans[x]+=1LL*(n-sum-1)*(sum+1)+n-1;//如果是割点 else ans[x]=2*n-2;//如果不是割点 } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); if(x==y)continue; add(x,y); add(y,x); } tarjan(1); for(int i=1;i<=n;i++) printf("%lld\n",ans[i]); return 0; }
rp++