链接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;
}