不知道为何,我第一次想到的式子居然就是正解式子;
设\(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]\)
如下代码:c++
#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; }