题解[HEOI2013]SAO.md

题意

给定一棵树,边有方向。要给点重标号,使得边都由小编号指向大编号,求重标号方案数。

\(n\le10^5\)

思路

有关树的计数。套路性考虑树形 DP。

设计状态 \(f_{u}\) 表示 \(u\) 的子树的合法重标号方案数。如何转移?发现不好转移。

考虑什么情况下可以转移呢?发现转移是合并当前 \(u\) 子树中已经考虑了部分的状态和其某还未考虑的儿子 \(v\) 的子树的状态,而转移合法只要保证 \(u\)\(v\) 在重标号中相对大小关系符合边上的限制。那么我们考虑改状态为 \(f_{u,i}\) 表示 \(u\) 的子树重标号且 \(u\) 的新编号为 \(i\) 的方案数。如何转移?

大于和小于的情况是对称的,所以我们只讨论 \(u\) 的新编号要小于 \(v\) 的情况,另一种情况的转移类似。我们考虑状态 \(f_{u,i}\) 能转移到什么状态。考虑在新的重标号方案中,在 \(v\) 的子树中拿 \(j\) 个点标上小于 \(i+j\) 的编号,\(u\) 标上编号 \(i+j\),那么考虑转移方程中需要哪些系数呢?首先,考虑 \(f_{u,i}\) 能与哪些 \(f_{v,k}\) 合并得到新的状态,发现 \(v\) 子树中前新编号前 \(j\) 小的点都拥有小于 \(i\) 的新编号,而 \(v\) 的新编号不能小于 \(u\),所以要保证 \(k>j\),也就说这次转移中合法的 \(v\) 的子树重编号方案数是 \(\sum\limits_{k=j+1}^{siz_v}f_{v,k}\)。其他系数?要在 \(i+j-1\) 个新编号小于 \(i+j\) 的点中选 \(j\) 个点来自 \(v\) 的子树,这里需要乘上 \(\binom{i+j-1}{j}\)。新编号大于 \(i+j\) 的同理,乘上 \(\binom{siz_u+siz_v-i-j}{siz_v-j}\)。总的式子就是 \(f_{u,i+j}+=\binom{i+j-1}{j}\binom{siz_u+siz_v-i-j}{siz_v-j}f_{u,i}\sum\limits_{k=j+1}^{siz_v}f_{v,k}\)。发现前缀和可以优化掉那个 \(\sum\)。统计答案就是 \(\sum\limits_{i=1}^nf_{1,i}\)

计算一下复杂度,看似是 \(O(n^3)\)?每次合并复杂度为 \(siz_u\times siz_v\),这与树形背包是一样的复杂度,所以总复杂度应该是 \(O(n^2)\)。可以理解为 \(u\) 子树中已考虑的每个点和 \(v\) 子树中每个点都 \(O(1)\) 计算一次,发现每对点都只会在其 LCA 处计算一次,所以总复杂度是点对数即 \(O(n^2)\)

实现

void DP(int u,int fa){
	sz[u]=1;
	for(int i=1;i<=n;++i)
		f[u][i]=0;
	f[u][1]=1;
	for(int ie=head[u],v=e[ie].t;ie;ie=e[ie].n,v=e[ie].t)
		if(v!=fa){
			DP(v,u);
			for(int i=1;i<=sz[u]+sz[v];++i)
				tmp[i]=0;
			if(e[ie].op)
				for(int i=1;i<=sz[u];++i)
					for(int j=1;j<=sz[v];++j)
						MAdd(tmp[i+j-1],Mul(Mul(Add(g[v][sz[v]],MOD-g[v][j-1]),f[u][i]),Mul(C(i+j-2,j-1),C(sz[u]-i+sz[v]-j+1,sz[v]-j+1))));
			else
				for(int i=1;i<=sz[u];++i)
					for(int j=1;j<=sz[v];++j)
						MAdd(tmp[i+j],Mul(Mul(g[v][j],f[u][i]),Mul(C(i+j-1,j),C(sz[u]-i+sz[v]-j,sz[v]-j))));
			sz[u]+=sz[v];
			for(int i=1;i<=sz[u];++i)
				f[u][i]=tmp[i];
		}
	for(int i=1;i<=sz[u];++i)
		g[u][i]=Add(g[u][i-1],f[u][i]);
}
posted @ 2022-10-31 22:28  Diwanul  阅读(25)  评论(0编辑  收藏  举报