• luogu P4099 [HEOI2013]SAO


    传送门

    吐槽题目标题

    这个依赖关系是个树,可以考虑树型dp,设f_i表示子树i的答案

    因为这是个序列问题,是要考虑某个数的位置的,所以设(f_{i,j})表示子树i构成的序列,i在第j个位置的方案.转移依次合并儿子(y),每次枚举一个位置j,以及枚举儿子(y)的序列中有k个数放在插前面,可以得到(f_{x,j+k}leftarrow f_{x,j}*w*inom{j-1+k}{k}*inom{sz_x+sz_y-j-k}{sz_y-k}),组合数即考虑插入的方式

    还有一个w不知道,如果要求(x)(y)前面,那么(y)要在自己子树序列的(k+1)位置及以后所以(w=sum_{i=k+1}^{sz_y} f_{y,i}),否则要在(k)及以前,所以(w=sum_{i=1}^{k} f_{y,i}),显然可以记前缀和

    最后答案为根的dp值总和.注意到合并复杂度,这些合并等价于每个点对都在lca处贡献一个(O(1)),所以总复杂度为(O(n^2))

    #include<bits/stdc++.h>
    #define LL long long
    #define db double
    #define il inline
    #define re register
    
    using namespace std;
    const int N=1000+10,mod=1e9+7;
    il int rd()
    {
        int x=0,w=1;char ch=0;
        while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
        return x*w;
    }
    int to[N<<1],nt[N<<1],w[N<<1],hd[N],tot=1;
    il void add(int x,int y,int z)
    {
        ++tot,to[tot]=y,nt[tot]=hd[x],w[tot]=z,hd[x]=tot;
        ++tot,to[tot]=x,nt[tot]=hd[y],w[tot]=z^1,hd[y]=tot;
    }
    int c[N][N],f[N][N],g[N],sz[N],pr[N][N],sf[N][N];
    void dp(int x,int ffa)
    {
        sz[x]=1;
        f[x][1]=1;
        for(int i=hd[x];i;i=nt[i])
        {
            int y=to[i];
            if(y==ffa) continue;
            dp(y,x),sz[x]+=sz[y];
            memset(g,0,sizeof(g));
            if(w[i]==0)
            {
                for(int j=sz[x]-sz[y];j;--j)
                    for(int k=0;k<=sz[y];++k)
                        g[j+k]=(g[j+k]+1ll*f[x][j]*c[j-1+k][k]%mod*c[sz[x]-j-k][sz[y]-k]%mod*sf[y][k+1]%mod)%mod;
            }
            else
            {
                for(int j=sz[x]-sz[y];j;--j)
                    for(int k=0;k<=sz[y];++k)
                        g[j+k]=(g[j+k]+1ll*f[x][j]*c[j-1+k][k]%mod*c[sz[x]-j-k][sz[y]-k]%mod*pr[y][k]%mod)%mod;
            }
            memcpy(f[x],g,sizeof(g));
        }
        for(int i=1;i<=sz[x];++i) pr[x][i]=(pr[x][i-1]+f[x][i])%mod;
        sf[x][sz[x]+1]=0;
        for(int i=sz[x];i;--i) sf[x][i]=(sf[x][i+1]+f[x][i])%mod;
    }
    
    int main()
    {
        for(int i=0;i<=1000;++i)
        {
            c[i][0]=1;
            for(int j=1;j<=i;++j) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
        }
        int T=rd();
        while(T--)
        {
            memset(f,0,sizeof(f));
            memset(hd,0,sizeof(hd)),tot=1;
            int n=rd();
            for(int i=1;i<n;++i)
            {
                int x=rd()+1,z=(getchar())=='>',y=rd()+1;
                add(x,y,z);
            }
            dp(1,0);
            int ans=0;
            for(int i=1;i<=n;++i) ans=(ans+f[1][i])%mod;
            printf("%d
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    进程 线程
    random模块 时间模块 sys模块 os模块 json模块 pickle模块
    异常处理
    面向对象进阶篇
    面向对象初级篇
    正则表达式
    re模块的相关知识
    CNN归纳偏好
    window和Linux下安装nvidia的apex
    使用GrabCut做分割
  • 原文地址:https://www.cnblogs.com/smyjr/p/10419651.html
Copyright © 2020-2023  润新知