• bzoj4784: [Zjoi2017]仙人掌


    道理我都懂,但是这个DP难度真的很大

    先tarjan一下判断是不是仙人掌

    对于环上的点,它们是不能相连的,直接拆了完事

    然后我们成功的把仙人掌转成了树,就是一个treeDP

    问题转化成在一棵树上,可以选择两个点将其树上路径覆盖,求不相交的覆盖方案数

    令f[x]表示x子树中的方案数,它没有一条单向向上的路径,g[x]表示有的方案数,这个比较容易想到

    但是如果让子节点的g两两匹配更新f[x],这样是极为复杂的,因为两两匹配数量是不定的

    我们没有办法快速统计在两两匹配数量不同的情况下的方案数。

    这是我遇到的瓶颈,%了网上的题解才知道以下做法:

    令h[i]表示有i个孩子匹配,可以不匹配的方案数

    f[x]=∏g[son]*h[子节点数]

    选择不匹配,相当于由这个孩子连向了这个根节点,如果是重边,理解为不连,相当于舍弃了这条向上的边

    这样就涵盖住所有的情况了:路径合并、某些子树不向上连(不必要把所有的边覆盖)

    通过不连,把匹配数量不定的情况合并了,换个理解,舍弃的边视为连了自环,把所有的边都覆盖了,而这个等价于舍弃

    同理g[x]=f[x]+∏g[son]*h[子节点数-1]*子节点数

    意思是首先x可以选择自己向上连,也可以选择一个孩子向上,方案为g[y],其他的就是∏(不包括y)g[son]*h[子节点数-1],有子节点数种选择

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    const LL mod=998244353;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while('0'>ch||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x;
    }
    
    struct node
    {
        int x,y,next;bool bk;
    }a[2100000];int len,last[510000];
    void ins(int x,int y)
    {
        len++;
        a[len].x=x;a[len].y=y;a[len].bk=true;
        a[len].next=last[x];last[x]=len;
    }
    int z,dfn[510000],fr[510000];bool bk;
    void dfs(int x)
    {
        if(bk==true)return ;
        dfn[x]=++z;
        for(int k=last[x];k;k=a[k].next)
            if(dfn[a[k].y]==0)fr[a[k].y]=k,dfs(a[k].y);
        
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(a[fr[y]].x!=x&&dfn[x]<dfn[y])
            {
                if(a[k].bk==false||a[k^1].bk==false){bk=true;return ;}
                a[k].bk=a[k^1].bk=false;
                int p=fr[y];
                while(y!=x)
                {
                    if(a[p].bk==false||a[p^1].bk==false){bk=true;return ;}
                    a[p].bk=a[p^1].bk=false;
                    y=a[p].x;p=fr[y];
                }
            }
        }
    }
    
    bool v[510000];
    LL f[510000],g[510000],h[510000];
    void treedp(int x,int fa)
    {
        v[x]=true;
        f[x]=1,g[x]=0; int s=0;
        for(int k=last[x];k;k=a[k].next)
        {
            if(!a[k].bk)continue;
            int y=a[k].y;
            if(v[y]==false)
            {
                treedp(y,x);
                f[x]=(f[x]*g[y])%mod;
                s++;
            }
        }
        g[x]=(f[x]*h[s]%mod+f[x]*h[s-1]%mod*s%mod)%mod;
        f[x]=f[x]*h[s]%mod;
    }
    int main()
    {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        h[0]=1;h[1]=1;for(int i=1;i<=500000;i++)h[i]=(h[i-1]+((i-1)*h[i-2])%mod)%mod;
        int T=read(),n,m,x,y;
        while(T--)
        {
            n=read(),m=read();
            len=1;for(int i=1;i<=n;i++)last[i]=dfn[i]=0,v[i]=false;
            for(int i=1;i<=m;i++)
            {
                x=read(),y=read();
                ins(x,y),ins(y,x);
            }
            z=0;bk=false;
            fr[1]=0;dfs(1);
            if(bk==true){printf("0
    ");continue;}
            
            
            LL ans=1;
            for(int i=1;i<=n;i++)
                if(v[i]==false)
                {
                    treedp(i,0);
                    ans=(ans*f[i])%mod;
                }
            printf("%lld
    ",ans);
        }
        
        return 0;
    }
  • 相关阅读:
    【转载】make: Nothing to be done for `all'. 解决方法
    P4行为模型BMV2安装
    P4行为模型BMV2依赖关系安装:thrift nanomsg nnpy安装
    P4factory ReadMe 剩余部分
    P4factory ReadMe Quickstart 安装p4factory
    Git 使用ssh密钥
    c++ 有swap函数
    c++ 引用
    topk两种解法
    xgboost和gbdt区别
  • 原文地址:https://www.cnblogs.com/AKCqhzdy/p/10180043.html
Copyright © 2020-2023  润新知