SAO题解
不知道为什么,我第一次想到的式子竟然就是正解式子;
设\(f[x][i]\)为x点在排在子树中权值第i个的方案数:
对\(\forall v \in son[x]\),x状态为:\(f[x][j]\),v状态为:\(f[v][k]\)
若\(w[v]<w[x]\),则至少有\(j+k\)个点\(<=w[x]\),至多有j+siz[v]个点,
则对固定状态\(f[x][j+k]\),可以从\(\sum_{i=1}^{k}f[v][i]\)转移过来,即有\(f[x][j]*sum[v][k]\)种方案(sum为前缀和),
但是,这只是选出\(j+k\)个小于等于x的权值的方案数,
两个有序表之间仍可以有不同顺序;
我们可以看成一个插进另一个,用隔板法,将一个有序表看成隔板,则是在\(j+k-1\)个小于\(w[x]\)的位置中选出j-1个位置放隔板,即是\(c[j+k-1][j-1]\);
但这还没完,
这只是小于\(w[x]\)的有序表,还有两个大于\(w[x]\)的,同理,可得:\(c[siz[x]-j+siz[v]-k][siz[x]-j]\)
所以转移方程为:
\(f[x][j+k]=f[x][j]*sum[v][k]*c[j+k-1][j-1]*c[siz[x]+siz[v]-j-k][siz[x]-j]\)
以下代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1006;
const ll mod=1e9+7;
int n,t,cnt=0,siz[N],head[N],t1,t2;
ll f[N][N],sum[N][N],c[N][N],tmp[N];
char s[12];
struct edge{int nxt,to,w;}e[N<<1];
inline void add(int u,int v,int w){e[++cnt].nxt=head[u],e[cnt].to=v,e[cnt].w=w,head[u]=cnt;}
inline int read(){
int T=0,F=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
return F*T;
}
void dfs(int x,int fa){
int v; siz[x]=1,f[x][1]=sum[x][1]=1;
for(int i=head[x];i;i=e[i].nxt){
if(e[i].to==fa) continue;
v=e[i].to,dfs(v,x);
for(int j=siz[x];j>=1;--j)
for(int k=siz[v];k>=0;--k){
if(e[i].w) tmp[j+k]=(tmp[j+k]+f[x][j]*sum[v][k]%mod*c[j+k-1][j-1]%mod*c[siz[x]+siz[v]-j-k][siz[x]-j]%mod)%mod;
else tmp[j+k]=(tmp[j+k]+f[x][j]*(sum[v][siz[v]]-sum[v][k]+mod)%mod*c[j+k-1][j-1]%mod*c[siz[x]+siz[v]-j-k][siz[x]-j]%mod)%mod;
}
siz[x]+=siz[v];
for(int j=1;j<=siz[x];++j) f[x][j]=tmp[j],tmp[j]=0;
}
for(int j=1;j<=siz[x];++j) sum[x][j]=(sum[x][j-1]+f[x][j])%mod;
}
int main(){
c[0][0]=1;
for(int i=1;i<=1000;++i){
c[i][0]=c[i][i]=1;
for(int j=1;j<i;++j) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
t=read();
while(t--){
n=read(),cnt=0,memset(head,0,sizeof(head)),memset(f,0,sizeof(f)),memset(sum,0,sizeof(sum));
for(int i=1;i<n;++i) t1=read()+1,scanf("%s",s),t2=read()+1,add(t1,t2,s[0]=='>'),add(t2,t1,s[0]=='<');
dfs(1,0),printf("%lld\n",sum[1][n]);
}
return 0;
}