• P4099 [HEOI2013]SAO


    链接P4099 [HEOI2013]SAO

    • 如果真的把这个题当作图去做就炸了……还是考虑树怎么做。
    • 因为这个不是选父亲才能选子树,他是树型依赖背包的升级版,存在选子树才能选父亲的情况。
    • (f_{i,j})表示(i)节点的子树,(i)号节点在这个子树的拓扑位置为(j)的方案数。
    • 这样的好处在于我们可以很方便的计算下面的组合数。
    • 类似与树型背包的转移,儿子(to)转移到(i)相当于原来的拓扑序(S)和儿子的拓扑序(T)进行合并。
    • 先枚举 (u)(v) ,考虑如何从 (f′_{i,u})(f_{to,p})合并到 (f_{i,u+v})
    • 也就是枚举(i)在第一个拓扑序的位置,枚举儿子的拓扑序列哪一些必须在(i)之前,哪一些必须在(i)之后。
    • 如果是儿子比父亲先,所以在(i)之前的(k)才能转移。
    • 方程即:

    [f_{u,i+j}=f_{u,i}*C_{i+j-1}^{j}*C_{S_u+S_v-i-j}^{S_v-j}*sum f_{v,k}*[kleq j] ]

    • 这是儿子在父亲之前的情况,前面两个组合数分别是(j)前面融入(i)前面和(j)后面融入(i)后面
    • 儿子在父亲后面同理。

    [f_{u,i+j}=f_{u,i}*C_{i+j-1}^{j}*C_{S_u+S_v-i-j}^{S_v-j}*sum f_{v,k}*[k>j] ]

    • 朴素转移(O(n^3)),后面的(sum)显然可以前缀和优化为(O(n^2))
    • 注意一个实现细节,就是儿子在父亲后面的情况枚举(j)要从(0)开始,因为存在一个都不放在(i)前面的合法方案,儿子在父亲前面则不需要考虑。
    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define R register int
    #define ll long long
    using namespace std;
    const int mod=1e9+7;
    const int N=1001;
    const int M=2001;
    int t,n,cnt,u,v,sz[N],hd[N],to[M],w[M],nt[M],g[N][N],f[N][N];
    int jic[M],inv[M],tmp[N];
    char c;
    int Qpow(R x,R y){
        R ans=1,bas=x;
        while(y){
            if(y&1)ans=1ll*ans*bas%mod;
            bas=1ll*bas*bas%mod,y>>=1;
        }return ans;
    }
    void link(R f,R t,R d){
        nt[++cnt]=hd[f],to[cnt]=t,w[cnt]=d,hd[f]=cnt;
    }
    int C(R x,R y){return 1ll*jic[y]*inv[x]%mod*inv[y-x]%mod;}
    void add(R &x,R y){x=(x+y>=mod?x+y-mod:x+y);}
    int gi(){
        R x=0,k=1;char c=getchar();
        while(c!='-'&&(c<'0'||c>'9'))c=getchar();
        if(c=='-')k=-1,c=getchar();
        while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();	
        return x*k;
    }
    void Dfs(R i,R fm){
        sz[i]=1,f[i][1]=1;
        for(R k=hd[i];k;k=nt[k]){
            if(to[k]==fm)continue;
            Dfs(to[k],i);
            for(R j=sz[i]+sz[to[k]];j>=1;--j)tmp[j]=0;
            if(w[k]){
                for(R u=1;u<=sz[i];++u)
                    for(R v=0;v<=sz[to[k]];++v){
                        R tik=1ll*f[i][u]*C(sz[to[k]]-v,sz[i]+sz[to[k]]-u-v)%mod;
                        tik=1ll*tik*C(v,u+v-1)%mod*(g[to[k]][sz[to[k]]]-g[to[k]][v]+mod)%mod;
                        add(tmp[u+v],tik);
                    }
            }
            else{
                for(R u=1;u<=sz[i];++u)
                    for(R v=1;v<=sz[to[k]];++v){
                        R tik=1ll*f[i][u]*C(sz[to[k]]-v,sz[i]+sz[to[k]]-u-v)%mod;
                        tik=1ll*tik*C(v,u+v-1)%mod*g[to[k]][v]%mod;
                        add(tmp[u+v],tik);
                    }
            }
            sz[i]+=sz[to[k]];
            for(R j=sz[i];j>=1;--j)f[i][j]=tmp[j];
        }
        for(R j=1;j<=sz[i];++j)g[i][j]=(g[i][j-1]+f[i][j])%mod;
    }
    void sol(){
        memset(hd,0,sizeof(hd));
        memset(g,0,sizeof(g));
        memset(f,0,sizeof(f));
        n=gi(),cnt=0;
        for(R i=1;i<n;++i){
    //		u=gi(),cin>>c,v=gi();
            u=gi()+1,cin>>c,v=gi()+1;
    //		cout<<u<<' '<<v<<' '<<c<<endl;
            if(c=='<')link(u,v,0),link(v,u,1);
            else link(u,v,1),link(v,u,0);
        }
        Dfs(1,0);R ans=0;
        for(R i=1;i<=n;++i)add(ans,f[1][i]);
        cout<<ans<<endl;
    }
    int main(){
        t=gi(),jic[0]=1,inv[0]=1;
        for(R i=1;i<N;++i)jic[i]=1ll*jic[i-1]*i%mod;
        inv[N-1]=Qpow(jic[N-1],mod-2);
        for(R i=N-2;i>=1;--i)inv[i]=1ll*inv[i+1]*(i+1)%mod;
        while(t--)sol();
        return 0;
    }
    
  • 相关阅读:
    Docker 部署 Nginx
    Docker 安装 Redis
    linux shell "2>&1"
    定时备份docker mysql
    SpringBoot 中拦截器和过滤器的使用
    SpringBoot WebMvcConfigurer
    springboot自定义参数解析HandlerMethodArgumentResolver
    mysql在linux下查看my.cnf位置的方法
    Linux下设置mysql允许远程连接
    Android项目实战(六十):修改项目包名
  • 原文地址:https://www.cnblogs.com/Tyher/p/9853000.html
Copyright © 2020-2023  润新知