bzoj3167 [Heoi2013]Sao
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3167
题意:
WelcometoSAO(StrangeandAbnormalOnline)。这是一个VRMMORPG,含有n个关卡。但是,挑战不同关卡的顺序是一
个很大的问题。有n–1个对于挑战关卡的限制,诸如第i个关卡必须在第j个关卡前挑战,或者完成了第k个关卡才
能挑战第l个关卡。并且,如果不考虑限制的方向性,那么在这n–1个限制的情况下,任何两个关卡都存在某种程
度的关联性。即,我们不能把所有关卡分成两个非空且不相交的子集,使得这两个子集之间没有任何限制。
数据范围
数据组数 T≤5,1≤n≤1000
题解:
和bzoj4033很像,可以看看这篇 树形DP进阶:一类非线性的树形DP问题(例题 BZOJ4403 BZOJ3167)
题意就是:
有n个位置,n-1个限制,形如:i > j 或 i < j,表示第i个位置的数小于/大于第j个位置的数,求满足条件的n的排列数。答案mod 1000000007。
真没想到是树形DP…
看起来像是n^2的树形DP,状态的转移大抵与子树大小相关,要考虑到子树合并带来的影响。
dp[i][j]表示当前i的子树中,i之前有j个比i小的数的方案数。
边界dp[i][0]=1
考虑转移。
对于要合并rt的子树u,
1、 rt>u
dp[rt][i+j]+=dp[rt][i]∗∑j−1k=0dp[u][k]∗C(i+jj)C(size[rt]+size[u]−i−j−1size[u]−j)
∑j−1x=0dp[u][k]
是因为在除了u外j-1个比rt小的数与u的大小关系不确定,故k取0~j-1皆可。
两个组合数分别定了新增j个小的插进去的顺序和,size[u]-j个大的插进去的顺序。
2、rt < u
dp[rt][i+j]+=dp[rt][i]∗∑size[u]−1k=jdp[u][k]∗C(i+jj)C(size[rt]+size[u]−i−j−1size[u]−j)
更新是要覆盖掉原来的内容,所以要用辅助数组。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1005;
const int mod=1000000007;
int T,n,head[N],to[2*N],nxt[2*N],w[2*N],num=0,C[N][N],size[N],sum[N][N],dp[N][N],f[N];
void init()
{
memset(head,0,sizeof(head)); num=0;
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
}
void build(int u,int v,int ww)
{
num++;
to[num]=v;
w[num]=ww;
nxt[num]=head[u];
head[u]=num;
}
void dfs(int u,int fa)
{
size[u]=1; dp[u][0]=1;
for(int ii=head[u];ii;ii=nxt[ii])
{
int v=to[ii]; if(v==fa) continue;
dfs(v,u);
for(int i=0;i<=size[u]+size[v];i++) f[i]=0;
if(w[ii]<0)
{
for(int i=size[u]-1;i>=0;i--)
for(int j=size[v];j>=0;j--)
{
int ret=1LL*dp[u][i]*sum[v][j-1]%mod;
ret=1LL*(1LL*ret*C[i+j][j]%mod)*C[size[u]+size[v]-i-j-1][size[v]-j]%mod;
f[i+j]=(f[i+j]+ret)%mod;
}
}
else
{
for(int i=size[u]-1;i>=0;i--)
for(int j=size[v]-1;j>=0;j--)
{
int sm=j==0?sum[v][size[v]-1]:(sum[v][size[v]-1]-sum[v][j-1]+mod)%mod;
int ret=1LL*dp[u][i]*sm%mod;
ret=1LL*(1LL*ret*C[i+j][j]%mod)*C[size[u]+size[v]-i-j-1][size[v]-j]%mod;
f[i+j]=(f[i+j]+ret)%mod;
}
}
size[u]+=size[v];
for(int i=0;i<=size[u];i++) dp[u][i]=f[i];
}
sum[u][0]=dp[u][0];
for(int i=1;i<size[u];i++) sum[u][i]=(sum[u][i-1]+dp[u][i])%mod;
}
int main()
{
scanf("%d",&T);
for(int i=0;i<=1000;i++)
for(int j=0;j<=i;j++)
{
if(j==0||i==j) C[i][j]=1;
else C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
while(T--)
{
scanf("%d",&n); init();
for(int i=1;i<n;i++)
{
int u,v,ww; char opt[5];
scanf("%d%s%d",&u,opt,&v);
ww= opt[0]=='<'? 1:-1;
build(u,v,ww); build(v,u,-ww);
}
dfs(1,1);
printf("%d\n",sum[1][n-1]);
}
return 0;
}