• [POI2008]BLO-Blockade(tarjan割点)


    题目链接:https://www.luogu.org/problemnew/show/P3469

    题意:在n个点m条边的无向图,求删掉一个点后,有多少个有序(x,y)由连通到不连通

    思路:

    分两种情况:

    1.点不为割点,答案就是(n-1)*2(这个点到其他的点,其他的点到这个点)

    2.点是割点,那么图被分成一些部分,答案就是每个部分点的个数与其他部分的个数乘积的和,再加上(n-1)*2(即上面的)

    举个例子:

     

    比如删掉割点3时

    每个部分与其他点的乘积的和:12,也可以这样计算:

    集合(1)    *   集合(2)  =1

    集合(1,2)  *   集合(4)=2

    集合(1,2,4)*   集合(5)=3  这里只算了一边,所以答案就是 6*2

    怎么实现?在搜索中加上去就行,看代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+10;
    struct node{
        int next,to;
    }edge[maxn*10];
    ll head[maxn],low[maxn],dfn[maxn];
    ll ans[maxn],size[maxn];//ans为答案,size[i]存储以i为根子树的大小 
    int n,m,cnt,tot;
    void add(int u,int v)
    {
        edge[cnt].to=v;
        edge[cnt].next=head[u];
        head[u]=cnt++;
    }
    
    void tarjan(int u)
    {
        low[u]=dfn[u]=++tot;
        ll z=0;//记录已求出割点后集合的大小,相当于上面列子左边的集合 
        size[u]=1;//初始 
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(!dfn[v])
            {
                tarjan(v);
                size[u]+=size[v];//没遍历过当然要加上子树的大小,来求出以u为根子树的大小 
                low[u]=min(low[u],low[v]);
                if(low[v]>=dfn[u])//v为割点 
                {
                    ans[u]+=(ll)z*size[v];//模拟上面例子乘的过程 
                    z+=size[v];//记得集合变大 
                }
            }
            else
                low[u]=min(low[u],dfn[v]);
        }
        ans[u]+=(ll)z*(n-1-z);//不要忘了,这是最后一步(不知道具体用处,个人理解是算不是割点时的答案) 
    }
    
    int main()
    {
        cnt=tot=0;
        memset(head,-1,sizeof(head));
        memset(low,0,sizeof(low));
        memset(dfn,0,sizeof(dfn));
        memset(ans,0,sizeof(ans));
        memset(size,0,sizeof(size));
        int x,y;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        tarjan(1);
        for(int i=1;i<=n;i++)
            printf("%lld
    ",(ans[i]+n-1)<<1);
        return 0;
    }
  • 相关阅读:
    linux 终端分屏命令vsp(转)
    ACE消息队列(转)
    iovec结构体定义及使用 (转)
    转: 写给想成为前端工程师的同学们 (from 360前端团队)
    转:苹果企业级开发者账号申请流程
    奇舞团的博客(360前端团队)
    腾讯开源组件
    转:android studio入门合集
    Raid分类说明 (from mongodb权威指南)
    转: linux下的自动对时
  • 原文地址:https://www.cnblogs.com/xiongtao/p/9971301.html
Copyright © 2020-2023  润新知