P4099 [HEOI2013]SAO
题目链接
题目链接
这道题建出模型之后就是一个树形图
首先我们不考虑有向边的关系
而是直接考虑树形\(dp\)
关键是怎么个树形\(dp\)
我们维护\(dp[u][i]\)表示当前\(u\)节点在已经遍历的子树中拓扑序排名为\(i\)的情况数
那么接下来就是经典的做法 合并\(u\)和子树\(v\)
我们假设当前\(u\)已经维护好的序列中排名为\(i\)
当前子树\(v\)已经维护好的序列中排名\(j\)
1.合并之后\(u\)排名比\(v\)靠前 为\(k\)
那么\(i≤k≤i+j-1\)
\[dp[u][k]+=C_{k-1}^{i-1}* C_{siz[u]+siz[v]-k}^{siz[u]-i}* dp[u][i]* dp[v][j]
\]
2.合并之后\(u\)排名比\(v\)靠后 为\(k\)
那么\(j+i≤k≤i+siz[v]\)
\[dp[u][k]+=C_{k-1}^{i-1}C_{siz[v]+siz[u]-k}^{Isz[u]-i}* dp[u][i]* dp[v][j]
\]
有上述我们可以发现 枚举\(i,j,k\)是\(O(n^3)\)的
所以我们需要优化
可以发现在上述\(dp\)式子中 只用一项涉及到了\(j\)
所以我们在确定了\(j\)以及\(k\)的范围之后
切换枚举顺序可以使用前缀和优化
\[1≤i≤siz[u]
\]
\[i≤k≤i+siz[v]-1
\]
\[∵i≤k≤i+j-1
\]
\[∴j≥k-i+1
\]
\[1≤i≤siz[u]
\]
\[i+1≤k≤i+siz[v]
\]
\[∵j+i≤k≤i+siz[v]
\]
\[∴j≤k-i
\]
所以我们可以使用前缀和优化
CODE:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<string>
#include<queue>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<deque>
#include<vector>
#include<ctime>
#define ll long long
#define inf 0x7fffffff
#define N 1008
#define IL inline
#define M 1008611
#define D double
#define mod 1000000007
#define R register
using namespace std;
template<typename T>IL void read(T &_)
{
T __=0,___=1;char ____=getchar();
while(!isdigit(____)) {if(____=='-') ___=0;____=getchar();}
while(isdigit(____)) {__=(__<<1)+(__<<3)+____-'0';____=getchar();}
_=___ ? __:-__;
}
char getch() {
char ch = getchar();
while(ch != '<' && ch != '>') ch = getchar();
return ch;
}
/*-------------OI使我快乐-------------*/
int T,n,tot;
int to[N<<1],nex[N<<1],head[N<<1],w[N<<1];
int siz[N];
ll C[N][N],dp[N][N],tmp[N];
IL void add(int x,int y,int z)
{to[++tot]=y;nex[tot]=head[x];head[x]=tot;w[tot]=z;}
IL void dp_cdy(int now,int v)
{
for(R int i=1;i<=n;++i) tmp[i]=dp[now][i],dp[now][i]=0;
for(R int i=1;i<=siz[now];++i)
for(R int j=i;j<=i+siz[v]-1;++j)
dp[now][j]=(dp[now][j]+C[j-1][i-1]*C[siz[now]+siz[v]-j][siz[now]-i]%mod*tmp[i]%mod*(dp[v][siz[v]]-dp[v][j-i]+mod)%mod)%mod;
}
IL void dp_wzy(int now,int v)
{
for(R int i=1;i<=n;++i) tmp[i]=dp[now][i],dp[now][i]=0;
for(R int i=1;i<=siz[now];++i)
for(R int j=i+1;j<=siz[v]+i;++j)
dp[now][j]=(dp[now][j]+C[j-1][i-1]*C[siz[now]+siz[v]-j][siz[now]-i]%mod*tmp[i]%mod*dp[v][j-i]%mod)%mod;
}
IL void dfs(int now,int fat)
{
siz[now]=1;dp[now][1]=1;
for(R int i=head[now];i;i=nex[i])
{
int v=to[i];
if(v==fat) continue;
dfs(v,now);
if(w[i]) dp_cdy(now,v);
else dp_wzy(now,v);
siz[now]+=siz[v];
}
for(R int i=1;i<=siz[now];++i) dp[now][i]=(dp[now][i]+dp[now][i-1])%mod;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(T);
for(R int i=0;i<=1000;++i) C[i][0]=1;
for(R int i=1;i<=1000;++i)
for(R int j=1;j<=i;++j)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
// for(R int i=1;i<=6;++i)
// for(R int j=1;j<=i;++j)
// printf("%lld%c",C[i][j],(j==i ? '\n':' '));
while(T--)
{
read(n);tot=0;
memset(dp,0,sizeof dp);
memset(head,0,sizeof head);
for(R int i=1,x,y;i<n;++i)
{
char z;
read(x);z=getch();read(y);++x;++y;
add(x,y,z=='<');add(y,x,z=='>');
// printf("%d %c %d\n",x,z,y);
}
dfs(1,0);
// for(R int i=1;i<=n;++i) printf("%d%c",siz[i],(i==n ? '\n':' '));
printf("%lld\n",dp[1][n]);
}
// fclose(stdin);
// fclose(stdout);
return 0;
}