P4099 [HEOI2013]SAO

题解

感觉是一道树型 \(\text{dp}\) 好题,反正不看题解不会做,我真屑。

题意很简单,给你一张有向树型图,问你拓扑序数量。

因为我们发现如果一棵子树的内部的拓扑序已经确定了,影响这个子树与外面的相对关系的只有根节点,所以我们定义 \(f_{i,j}\)\(i\) 在以 \(i\) 为根的子树中拓扑序位置为 \(j\) 的方案数。

然后有了这个东西之后我们发现转移就很简单了。

\(u<v\) ,则:

\[(new)f_{u,k}=\binom{k-1}{i-1}\binom{size_u+size_v-k}{size_u-i}\cdot f_{u,i}\sum_{j=k-i+1}^{size_v}f_{v,j} \]

\(u>v\) ,则:

\[(new)f_{u,k}=\binom{k-1}{i-1}\binom{size_u+size_v-k}{size_u-i}\cdot f_{u,i}\sum_{j=1}^{k-i}f_{v,j} \]

好像做完了。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e3+5;
const int MOD=1e9+7;
int n,siz[N];
int f[N][N],g[N][N],h[N];
int frac[N],ifrac[N];
int ksm(int x,int k){
	int res=1;
	for(;k;k>>=1,x=x*x%MOD)
	if(k&1) res=res*x%MOD;
	return res;
}
int cal(int n,int m){
	if(n<0||m<0||n<m) return 0;
	return frac[n]*ifrac[m]%MOD*ifrac[n-m]%MOD;
}
struct Edge{int nxt,to,flag;}e[N<<1];int fir[N];
void add(int u,int v,int w,int i){e[i]=(Edge){fir[u],v,w},fir[u]=i;}
void dp(int u,int fa){
	siz[u]=1,f[u][1]=1;
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa) continue;
		dp(v,u),memset(h,0,sizeof(h));
		for(int c=1;c<=siz[u];++c){
			for(int d=1;d<=siz[u]+siz[v];++d){
				if(e[i].flag){
					if(d-c+1>siz[v]) continue;
					h[d]+=cal(d-1,c-1)*cal(siz[u]+siz[v]-d,siz[u]-c)%MOD*f[u][c]%MOD*(g[v][siz[v]]-g[v][d-c]+MOD)%MOD;
					h[d]%=MOD;
				}
				else{
					if(1>d-c) continue;
					h[d]+=cal(d-1,c-1)*cal(siz[u]+siz[v]-d,siz[u]-c)%MOD*f[u][c]%MOD*(g[v][d-c]-g[v][0]+MOD)%MOD;
					h[d]%=MOD;
				}
			}
		}
		siz[u]+=siz[v];
		for(int j=1;j<=siz[u];++j) f[u][j]=h[j];
	}
	// printf("%lld\n",u);
	// for(int i=1;i<=siz[u];++i) printf("%lld ",f[u][i]);
	// printf("\n");
	for(int i=1;i<=siz[u];++i){
		g[u][i]=(g[u][i-1]+f[u][i])%MOD;
	}
}
void solve(){
	cin>>n;
	for(int i=1;i<n;++i){
		int u,v;char s[2];
		scanf("%lld%s%lld",&u,s,&v),u++,v++;
		add(u,v,s[0]=='<',i<<1);
		add(v,u,s[0]=='>',i<<1|1);
	}
	dp(1,0);
	int res=0;
	for(int i=1;i<=n;++i) res+=f[1][i],res%=MOD;
	return printf("%lld\n",res),void();
}
void init(){
	memset(fir,0,sizeof(fir));
	memset(g,0,sizeof(g));
}
signed main(){
	frac[0]=1;
	for(int i=1;i<N;++i) frac[i]=frac[i-1]*i%MOD;
	ifrac[N-1]=ksm(frac[N-1],MOD-2);
	for(int i=N-1;i>=1;--i) ifrac[i-1]=ifrac[i]*i%MOD;
	int T;cin>>T;
	while(T--) init(),solve();
}
posted @ 2021-04-17 16:34  Point_King  阅读(57)  评论(0编辑  收藏  举报