题意:
给出一个n个点m条边的无向图;
求将每个点的所有边删去之后,这个图中的不能到达的点对数;
n<=100000,m<=500000;
题解:
Poi~
要做这题首先样例要看懂= =;
显然不能到达的点对数=n*n-能到达的点对数;
而对于每个连通块来说,能到达的点数为块内点数的平方;
那么就对于一个点,统计删去边后,每个连通块的大小;
所以构造DFS树,所有的非树边都是返祖边;
记录所有点的深度deep,子树大小size,和它与它的子树的返祖边,能到达最小深度top;
那么统计答案时枚举枚举儿子;
如果儿子有高于它的返祖边,那么这颗子树与父树连通,记录父树连通块大小;
由于儿子的子树之间不可能有非树边连通,那么如果儿子没有返祖边就直接记录,ans-=size*size;
最后将父树连通块的点对数减去就好;
注意开long long,别忘了它自身也是一个连通块;
时间复杂度O(n),这个算法似乎是Tarjan。。
但是最近遇到的Tarjan都被改的不成样子= =(或者说都被DFS树水过去了);
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 131072
#define M 1010000
using namespace std;
typedef long long ll;
int next[M],to[M],head[N],ce;
int n,size[N],top[N],deep[N],tot;
ll ans[N];
void add(int x,int y)
{
to[++ce]=y;
next[ce]=head[x];
head[x]=ce;
}
void tarjan(int x,int fa)
{
top[x]=deep[x]=deep[fa]+1;
size[x]=1;
int i,y;
for(i=head[x];i;i=next[i])
{
if(!deep[y=to[i]])
tarjan(y,x),top[x]=min(top[y],top[x]),size[x]+=size[y];
else
top[x]=min(top[x],deep[y]);
}
ans[x]=(ll)n*n-1;
int temp=n-size[x];
for(i=head[x];i;i=next[i])
{
if(deep[y=to[i]]==deep[x]+1)
{
if(top[y]<deep[x])
temp+=size[y];
else
ans[x]-=(ll)size[y]*size[y];
}
}
ans[x]-=(ll)temp*temp;
}
int main()
{
int m,i,j,k,x,y,fx,fy;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
tarjan(1,0);
for(i=1;i<=n;i++)
{
printf("%lld\n",ans[i]);
}
return 0;
}