bzoj1123:[POI2008]BLO

传送门

提示:被删掉的点也要算点对,\((i,j)\)\((j,i)\)是不同的点对

显然找出割点就行了,记下size,对于各子树统计一下答案
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void read(int &x) {
    char ch; bool ok;
    for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=1e5+10;
int n,m,size[maxn],tot,now,pre[maxn*10],nxt[maxn*10],rt,h[maxn],cnt,dfn[maxn],low[maxn],id;bool cur[maxn],vis[maxn];
long long ans[maxn];
void add(int x,int y)
{
    pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt,
    pre[++cnt]=x,nxt[cnt]=h[y],h[y]=cnt;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++id,size[x]=1;int sum=0,flag=0;
    for(rg int i=h[x];i;i=nxt[i])
    {
        if(!dfn[pre[i]])
        {
            tarjan(pre[i]),size[x]+=size[pre[i]],low[x]=min(low[pre[i]],low[x]);
            if(low[pre[i]]>=dfn[x])
            {
                flag++,sum+=size[pre[i]];
                ans[x]+=1ll*size[pre[i]]*(n-size[pre[i]]);
                if(x!=rt||flag>1)cur[x]=1;
            }
        }
        else low[x]=min(low[x],dfn[pre[i]]);
    }
    if(cur[x])ans[x]+=1ll*(n-sum-1)*(sum+1)+n-1;
    else ans[x]=2*n-2;
}
int main()
{
    read(n),read(m);
    for(rg int i=1,x,y;i<=m;i++)read(x),read(y),add(x,y);
    for(rg int i=1;i<=n;i++)if(!dfn[i])rt=i,tarjan(i);
    for(rg int i=1;i<=n;i++)printf("%lld\n",ans[i]);
}

猜你喜欢

转载自www.cnblogs.com/lcxer/p/10390577.html