洛谷P4099 [HEOI2013]SAO
题解
我们可以把有关系的二元组连接,不考虑方向的话就是一棵树。所以我们可以做树形 $\text{dp}$ : $f[i][j]$ 表示 $i$ 子树中 $i$ 的排名是 $j$ 的方案数,然后考虑转移。如果 $v$ 要在 $u$ 前的话,那就是:$$f[u][i+j]←\sum_{k=1}^{j} f[u][i]\times f[v][k]\times (_{i+j-1}^{j}) \times (_{sz_u+sz_v-i-j}^{sz_u-i})$$
如果 $u$ 在 $v$ 前的话也是类似的。于是我们就只要对 $f[u][i]$ 做前缀和,即可将效率降至 $O(n^2)$ 。
代码
#include <bits/stdc++.h> using namespace std; const int N=1005,M=N<<1,P=1e9+7;char ch[3]; int T,n,c[N][N],hd[N],V[M],nx[M],W[M],t,f[N][N],sz[N],F[N]; int X(int x){return x>=P?x-P:x;} void add(int u,int v,int w){ nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w; } void dfs(int u,int fr){ f[u][1]=sz[u]=1; for (int v,i=hd[u];i;i=nx[i]){ if ((v=V[i])==fr) continue;dfs(v,u); for (int j=1;j<=sz[u];j++) for (int k=0;k<=sz[v];k++) if (W[i]) F[j+k]=X(F[j+k]+1ll*f[u][j]*f[v][k]%P*c[j+k-1][k]%P*c[sz[u]+sz[v]-j-k][sz[u]-j]%P); else F[j+k]=X(F[j+k]+1ll*f[u][j]*(f[v][sz[v]]-f[v][k]+P)%P*c[j+k-1][k]%P*c[sz[u]+sz[v]-j-k][sz[u]-j]%P); sz[u]+=sz[v]; for (int j=1;j<=sz[u];j++) f[u][j]=F[j],F[j]=0; } for (int j=1;j<=sz[u];j++) f[u][j]=X(f[u][j]+f[u][j-1]); } void work(){ scanf("%d",&n); for (int i=1,u,v;i<n;i++) scanf("%d%s%d",&u,ch,&v),u++,v++, add(u,v,ch[0]=='>'),add(v,u,ch[0]=='<'); dfs(1,0);printf("%d\n",f[1][n]); for (int i=1;i<=n;i++) hd[i]=0;t=0; } int main(){ c[0][0]=1; for (int i=1;i<N;i++){ c[i][0]=1; for (int j=1;j<=i;j++) c[i][j]=X(c[i-1][j]+c[i-1][j-1]); } for (cin>>T;T--;work());return 0; }