【题目描述】
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
Sample Output
8
8
16
14
8
【题目分析】
题目让求删去每个点以后会形成多少不能联通的城市对。
我们很容易联想到强连通分量求割点,割点肯定会形成很多个不能连通的城市对,而不是割点的话除了自身就没有其他不能连通的。(仔细研究样例就能发现自身和其他城市也算在内)
可是问题是对于每一个割点怎么来求多少个城市呢?
首先:割点上不同子树之间肯定无法连通
其次:割点的子树和除割点外的其他点无法联通
最后:对于每个被删除的点都和其他点无法联通
需要注意的是每个点对都要计算两次(这也是研究样例得到的)
最后一个很简单,就是(n-1)*2(我们后面的讨论都不讨论乘2,最后统一乘起来就是)
其次中的我们只需要统计每个割点的所有子树上总共有多少个点tmp,那么(n-1-tmp)*tmp就是答案
重点来看对于首先应该怎样解决。我们不妨现想一下如何统计每个割点的子树的节点,肯定是在搜索子树后再回溯得到每一棵子树的节点的个数,然后加起来。对于当前这个子树(节点数num[v]),他和前面所有的子树节点(节点数tmp=num[v1]+num[v2]+…+num[vi-1])都不连通,所以我们让答案加上num[v]和前面子树节点和的乘积,再更新子树节点和就好(详见代码Trajan函数部分)
【AC代码】
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAXN=1e5+5;
const int MAXM=5e5+5;
struct node
{int v,next;
}Edge[MAXM<<1];
int head[MAXN],tot;
int DFN[MAXN],LOW[MAXN];
//bool cnt[MAXN];
int vis[MAXN],Time;
ll ans[MAXN],n,m;
ll num[MAXN];void init()
{memset(head,0,sizeof(head));tot=0;memset(vis,0,sizeof(vis));Time=0;//memset(cnt,0,sizeof(cnt));memset(ans,0,sizeof(ans));memset(num,0,sizeof(num));
}void AddEdge(int u,int v)
{tot++;Edge[tot].v=v; Edge[tot].next=head[u];head[u]=tot;
}void read()
{int u,v;for(int i=0;i<m;i++){scanf("%d%d",&u,&v);AddEdge(u,v); AddEdge(v,u);}
}void Trajan(int x,int fa)
{int v; ll tmp=0;int son=0;DFN[x]=LOW[x]=++Time;vis[x]=1; num[x]=1;for(int i=head[x];i;i=Edge[i].next){v=Edge[i].v;if(vis[v]==0){Trajan(v,x);num[x]+=num[v];son++;LOW[x]=min(LOW[x],LOW[v]);//if(x==root &&son>1 || x!=root && LOW[v]>=DFN[x])if(LOW[v]>=DFN[x]) //这里不需要判断是否是根节点,因为如果如果是根节点只有一个子树的话tmp为0,ans[x]为0,要是有多个子树就是割点,直接计算。{ans[x]+=tmp*num[v];tmp+=num[v];//cnt[x]=true;}}else if(vis[v]==1){LOW[x]=min(LOW[x],DFN[v]);}}vis[x]==2;ans[x]+=tmp*(n-tmp-1);
}void solve()
{for(int i=1;i<=n;i++){if(!vis[i]){Trajan(i,i);}}
}void print()
{for(int i=1;i<=n;i++){printf("%lld\n",2ll*(ans[i]+n-1));}
}int main()
{while(~scanf("%lld%lld",&n,&m)){init();read();solve();print();}return 0;
}