题解 P4099 【[HEOI2013]SAO 】

devout

2020-11-10 19:57:23

Solution

> Welcome to SAO ( Strange and Abnormal Online) **description** 给你一棵有向树,问这棵树的拓扑序的情况数。 **solution** 考虑树形dp,我们用 $f[u][i]$ 表示以 $u$ 为根的子树中,$u$ 的拓扑序排名为 $i$ 的方案数 考虑 $u$ 的新的一个子节点时,对于边的方向分类讨论,假设 $u$ 要在 $v$ 之后。 那么可以推出转移: $$f[u][i+k]=\sum_{j\leq k} f[u][i]\times f[v][j]\times\binom{i+k-1}{i-1}\times\binom{siz_u+siz_v-i-k}{siz_u-i}$$ 解释一下,$f[u][i+k]$ 要从 $f[u][i]$ 和 $f[v][j]$ 转移过来的话,那么以 $v$ 为根的子树中应该有 $k$ 个点拓扑序在 $u$ 之前,合并之后在 $u$ 前面有 $i-k-1$ 个位置,任意从中选出 $i-1$ 个放 $f[u][i]$ 中在 $i$ 之前的,剩下的放在 $v$ 子树中的,就是 $\binom{i+k-1}{i-1}$,相应的,放在 $u$ 之后的方案数就是 $\binom{siz_u+siz_v-i-k}{siz_u-i}$ 这样朴素的转移是 $O(n^3)$ 的,观察发现我们每次的 $j$ 要满足 $j\leq k$,所以对于 $f[v][j]$ 求前缀和就可以把复杂度做到 $O(n^2)$ 了。 对于 $u$ 在 $v$ 之前的情况,类似的方法。 但是注意我们需要另外开一个数组来做转移,否则会被反复更新。 **code** ``` #include <bits/stdc++.h> using namespace std; # define Rep(i,a,b) for(int i=a;i<=b;i++) # define _Rep(i,a,b) for(int i=a;i>=b;i--) # define RepG(i,u) for(int i=head[u];~i;i=e[i].next) typedef long long ll; const int N=1005; const int mod=1e9+7; template<typename T> void read(T &x){ x=0;int f=1; char c=getchar(); for(;!isdigit(c);c=getchar())if(c=='-')f=-1; for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0'; x*=f; } int t,n; int head[N],cnt; int siz[N]; int f[N][N],g[N],h[N]; int fac[N],inv[N]; struct Edge{ int to,next,w; }e[N<<1]; void add(int x,int y,int c){ e[++cnt]=(Edge){y,head[x],c},head[x]=cnt; } int Qpow(int base,int ind){ int res=1; while(ind){ if(ind&1)res=1ll*res*base%mod; base=1ll*base*base%mod; ind>>=1; } return res; } int C(int n,int m){ return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod; } void dfs(int u,int fa){ siz[u]=1; f[u][1]=1; RepG(en,u){ int v=e[en].to; if(v==fa)continue; dfs(v,u); Rep(i,1,siz[u]+siz[v])h[i]=0; if(e[en].w<0){ g[0]=0; Rep(j,1,siz[v])(g[j]=g[j-1]+f[v][j])%=mod; Rep(i,1,siz[u]) Rep(k,1,siz[v]) (h[i+k]+=1ll*f[u][i]*g[k]%mod*C(i+k-1,i-1)%mod*C(siz[u]+siz[v]-i-k,siz[u]-i)%mod)%=mod; } else{ g[siz[v]+1]=0; _Rep(j,siz[v],1)(g[j]=g[j+1]+f[v][j])%=mod; Rep(i,1,siz[u]) Rep(k,0,siz[v]) (h[i+k]+=1ll*f[u][i]*g[k+1]%mod*C(i+k-1,i-1)%mod*C(siz[u]+siz[v]-i-k,siz[u]-i)%mod)%=mod; } siz[u]+=siz[v]; Rep(i,1,siz[u])f[u][i]=h[i]; } } int main() { read(t); fac[0]=inv[0]=1; Rep(i,1,1000)fac[i]=1ll*fac[i-1]*i%mod; Rep(i,1,1000)inv[i]=Qpow(fac[i],mod-2); while(t--){ memset(head,-1,sizeof(head)),cnt=0; memset(f,0,sizeof(f)); read(n); Rep(i,1,n-1){ int x,y; char opt[10]; scanf("%d%s%d",&x,opt,&y); x++,y++; if(opt[0]=='<')add(x,y,1),add(y,x,-1); else add(x,y,-1),add(y,x,1); } dfs(1,0); int ans=0; Rep(i,1,n)(ans+=f[1][i])%=mod; printf("%d\n",ans); } return 0; } ```