zoukankan      html  css  js  c++  java
  • P3225 [HNOI2012]矿场搭建

    传送门

    对于一个点双联通分量,如果它连接了两个或更多割点

    那么不论哪个点GG都有至少一条路通到其他的点双联通分量,所以我们不用考虑

    如果它只连接一个割点,如果这个割点GG,那整个块也一起GG,所以要再这个块里建一个出口

    如果它没有连接割点,只建一个出口还不够,可能这个出口所在的点GG,所以要两个出口

    那么三种情况的各自的方案数分别为

    1 (啥都不干)

    块内点的大小 (块内的每个点都可以建出口)

    $C_{块内点的大小}^2$ (块内顺便选两个点建出口)

    根据乘法原理把每个块的方案数乘起来就是总方案数了

    然后Tarjan求出所有割点和点双联通分量就好了

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=1e4+7;
    int fir[N],from[N<<1],to[N<<1],cntt;
    inline void add(int &a,int &b)
    {
        from[++cntt]=fir[a];
        fir[a]=cntt; to[cntt]=b;
    }
    int dfs_clock,dfn[N],low[N],st[N],Top,cut[N],rt,tot;
    vector <int> be[N];//be[i][j]存属于第i个块的第j个点
    void Tarjan(int x)//Tarjan模板求割点
    {
        dfn[x]=low[x]=++dfs_clock; st[++Top]=x;
        int ch=0;
        for(int i=fir[x];i;i=from[i])
        {
            int &v=to[i];
            if(!dfn[v])
            {
                Tarjan(v); ch++;
                low[x]=min(low[x],low[v]);
                if((x==rt&&ch>1)||(x!=rt&&dfn[x]<=low[v])) cut[x]=1;//注意节点为根时要特判
                if(dfn[x]<=low[v])
                {
                    be[++tot].clear();//注意多组数据
                    while(st[Top]!=v)
                        be[tot].push_back(st[Top--]);
                    be[tot].push_back(st[Top--]);
                    be[tot].push_back(x);//把割点也算进相邻的点双,方便统计
                }
            }
            else low[x]=min(low[x],dfn[v]);
        }
    }
    int n,m;
    ll ans1,ans2;
    int main()
    {
        int a,b,num=0;
        while(scanf("%d",&m)&&m)
        {
            memset(cut,0,sizeof(cut));
            memset(dfn,0,sizeof(dfn));
            memset(low,0,sizeof(low));
            memset(fir,0,sizeof(fir));
            n=tot=dfs_clock=Top=cntt=ans1=0; ans2=1;//初始化
            for(int i=1;i<=m;i++)
            {
                a=read(); b=read();
                add(a,b); add(b,a);
                n=max(n,max(a,b));//节点数还要自己求
            }
            for(int i=1;i<=n;i++) if(!dfn[i]) rt=i,Tarjan(i);
            for(int i=1;i<=tot;i++)
            {
                int len=be[i].size(),cnt=0;
                for(int j=0;j<len;j++) cnt+=cut[be[i][j]];//记录割点个数
                if(!cnt)//如果没割点
                    ans1+=2,ans2*=((1ll*len*(len-1))>>1);
                else if(cnt==1) ans1++,ans2*=(len-1);//注意len-1,因为len包括一个割点
            }
            printf("Case %d: %lld %lld
    ",++num,ans1,ans2);
        }
        return 0;
    }
  • 相关阅读:
    [csp-201509-3]模板生成系统
    [csp-201403-3]命令行选项
    [csp-201809-4]再卖菜 差分约束or记忆化搜索
    [转]相互引用的结构体的定义
    【转】宏定义中#和##的使用
    Linux系统目录结构
    Linux sh脚本用spool导出oracle数据库指定表表数据
    关于./xhost: unable to open display问题的解决
    查新系统软硬信息
    文件用户及用户组归属修改
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9875525.html
Copyright © 2011-2022 走看看