bzoj 1123: [POI2008]BLO

题意:

给一个连通无向图,问删掉i号点后有多少点对不相连。

题解:

双连通分量的例题。
首先至少有2*(n-1)(删掉的点也算入点对)
非割点就是这样
考虑割点
因为点双缩点后一定是一棵树,所以可以在树上计数。
我是这么做的,分别计算下所有点双中的点数的和,割点的度数和,还有割点的个数,那么就可以得出一个子树不同的点的个数(可能此方法很sb
然后就可以计数了。

update:实际上就是最傻逼的打法

其实直接在tarjan的时候乘就可以了,建议看hzwer的。
code:

#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#define LL long long
using namespace std;
struct node{
    int y,next;
}A[1000010],a[400010];int Len=0,Last[100010],len=0,last[200010];
vector<int> V[1000010];
int low[100010],dfn[100010],sta[100010],id=0,tp=0,cnt,num[200010],bel[100010];
LL ans[100010];
bool v[100010],g[100010];
struct trnode{
    int g,cnt,num;
}tr[200010];
void insert(int x,int y)
{
    A[++Len].y=y;
    A[Len].next=Last[x];Last[x]=Len;
}
void ins(int x,int y)
{
    a[++len].y=y;
    a[len].next=last[x];last[x]=len;
}
int n,m;
void dfs(int x)
{
    dfn[x]=low[x]=++id;
    sta[++tp]=x;v[x]=true;
    int tot=0;
    for(int i=Last[x];i;i=A[i].next)
    {
        int y=A[i].y;
        if(dfn[y]==-1)
        {
            dfs(y);tot++;
            low[x]=min(low[x],low[y]);
            if(low[y]==dfn[x])
            {
                g[x]=true;bel[x]=++cnt;
                V[x].push_back(cnt);
                num[cnt]=1;
                while(sta[tp+1]!=y)
                {
                    int z=sta[tp];
                    if(g[z]) V[z].push_back(cnt);
                    bel[z]=cnt;v[z]=false;tp--;
                    num[cnt]++;
                }
            }
        }
        else if(v[y]) low[x]=min(low[x],dfn[y]);
    }
    if(x==1&&tot==1) g[x]=false;
}
int getsum(int x) {return tr[x].num-tr[x].cnt+tr[x].g;}
void solve(int x,int fa)
{
    if(x<=n) tr[x].g++;
    else tr[x].num=num[x];
    for(int i=last[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(y==fa) continue;
        solve(y,x);
        tr[x].g+=tr[y].g;tr[x].num+=tr[y].num;
        tr[x].cnt+=tr[y].cnt;tr[x].cnt++;
    }
    int son=getsum(x);
    if(x<=n)
        for(int i=last[x];i;i=a[i].next)
        {
            int y=a[i].y;
            if(y==fa) continue;
            int sum=getsum(y);
            ans[x]+=(LL)(sum-1)*(LL)(n-son)*2;
            ans[x]+=(LL)(sum-1)*(LL)(son-sum);
        }
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;scanf("%d %d",&x,&y);
        insert(x,y);insert(y,x);
    }
    cnt=n;
    memset(v,false,sizeof(v));
    memset(dfn,-1,sizeof(dfn));
    memset(g,false,sizeof(g));
    dfs(1);
    for(int x=1;x<=n;x++)
        if(g[x])
            for(int i=0;i<V[x].size();i++) ins(x,V[x][i]),ins(V[x][i],x);
    if(g[1]) solve(1,0);
    else solve(bel[1],0);
    for(int i=1;i<=n;i++)
        printf("%lld\n",ans[i]+(LL)(n-1)*2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值