码迷,mamicode.com
首页 > 其他好文 > 详细

P4099 [HEOI2013]SAO

时间:2018-10-25 21:42:25      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:get   怎么   jic   har   行合并   表示   优化   math   eof   

链接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}*[k\leq 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;
}

P4099 [HEOI2013]SAO

标签:get   怎么   jic   har   行合并   表示   优化   math   eof   

原文地址:https://www.cnblogs.com/Tyher/p/9853000.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!