不知道爲何,我第一次想到的式子居然就是正解式子;
設\(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; }