【杂谈】树上拓扑序序列问题

220 篇文章 2 订阅
98 篇文章 0 订阅

专用于解决 “树形拓扑序序列” 问题。

在这种问题里,我们可以枚举两个节点计算其贡献,而且两个节点分别作为两棵树的根时的答案容易获得。

此时定义 f ( a , b ) f(a,b) f(a,b) 为,认为 a , b a,b a,b 的子树内的元素已定序时, l c a ( a , b ) lca(a,b) lca(a,b) 子树内的拓扑序数量。这也等同于,在 s l c a s_{lca} slca 个位置中,最后留下 a a a b b b 的子树对应的 “坑位”。

特别地,规定 p a p_a pa b b b 之前出现,且 p b p_b pb a a a 之前出现。不难发现它实际上是对 path ( a , l c a ) \text{path}(a,lca) path(a,lca) path ( b , l c a ) \text{path}(b,lca) path(b,lca) 作归并。

转移是
f ( a , b ) = f ( p a , b ) g ( bro a ) ( s a + b + s bro a s a + b ) + ⋯ f(a,b)=f(p_a,b)g(\text{bro}_a){s_{a+b}+s_{\text{bro}_a}\choose s_{a+b}}+\cdots f(a,b)=f(pa,b)g(broa)(sa+bsa+b+sbroa)+

其中 ⋯ \cdots 即将 a , b a,b a,b 换位的结果。其中 p a p_a pa a a a 的父节点, bro a \text{bro}_a broa a a a 唯一的兄弟节点, s s s 为子树所需要的 “坑位” 的数量——它并不一定是子树大小。

由于规定了 p a p_a pa b b b 之前出现,而事实上可能并非如此,因此答案可能需要枚举 a a a 的祖先计算贡献。——如果系数较简单,可以先累加系数再直接统计。

因此我们立刻获得了下列问题的解答:

Example 1. 求所有拓扑序序列的逆序对数量的和。

Solution 1. 我们有一种更简单的做法。但是这也不是不行。代码见文末。

Example 2. 求所有拓扑序序列 { p i } \{p_i\} {pi} ∑ i = 1 n − 1 ∣ p i − p i + 1 ∣ \sum_{i=1}^{n-1}|p_i-p_{i+1}| i=1n1pipi+1 的和。

Solution 2. 甚至不需要枚举 a a a 的祖先了。完整叙述可见于兔兔的博客

显然这个做法可以拓展到 O ( k n k ) \mathcal O(kn^k) O(knk) k k k 个元素之间的贡献。但那样代码就不太好写了。

附录

solution 1 \text{solution 1} solution 1 的代码。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MAXN = 4100;
struct Edge{
	int to, nxt;
	Edge(int _t,int _n):to(_t),nxt(_n){}
	Edge() = default;
};
Edge e[MAXN<<1];
int head[MAXN], cntEdge;
void addEdge(int a,int b){
	e[cntEdge] = Edge(b,head[a]);
	head[a] = cntEdge ++;
}

const int MOD = 1e9+7;
inline int qkpow(llong b,int q){
	llong a = 1;
	for(; q; q>>=1,b=b*b%MOD)
		if(q&1) a = a*b%MOD;
	return int(a);
}
int c[MAXN][MAXN], cnt[MAXN][MAXN];
int siz[MAXN], dp[MAXN], fa[MAXN], n;
void scan(int x,int pre){
	siz[x] = 0, dp[x] = 1, fa[x] = pre;
	for(int i=head[x]; ~i; i=e[i].nxt){
		if(e[i].to == pre) continue;
		scan(e[i].to,x); siz[x] += siz[e[i].to];
		dp[x] = int(llong(dp[x])*dp[e[i].to]%MOD
			*c[siz[x]][siz[e[i].to]]%MOD);
		rep(j,1,n) cnt[x][j] += cnt[e[i].to][j];
	}
	++ siz[x], ++ cnt[x][x]; // itself
}

int val[MAXN];
void solve(int x,int pre){
	siz[x] = 0; int sum = 0; dp[x] = 1;
	for(int i=head[x]; ~i; i=e[i].nxt){
		if(e[i].to == pre) continue;
		solve(e[i].to,x); siz[x] += siz[e[i].to];
		sum = int((llong(sum)*dp[e[i].to]%MOD
			+llong(dp[x])*val[e[i].to]%MOD)
			*c[siz[x]][siz[e[i].to]]%MOD);
		dp[x] = int(llong(dp[x])*dp[e[i].to]%MOD
			*c[siz[x]][siz[e[i].to]]%MOD);
	}
	++ siz[x], val[x] = (val[x]+sum)%MOD;
	val[x] = int((val[x]+llong(cnt[x][x-1])*dp[x])%MOD);
}

int invc[MAXN][MAXN], jc[MAXN], inv[MAXN];
int invdp[MAXN], lca[MAXN][MAXN], f[MAXN][MAXN];

bool cmp(const int &x,const int &y){
	return siz[x] > siz[y];
}
int id[MAXN];
int main(){
	n = readint(); int rt = readint();
	rep(i,0,n) rep(j,c[i][0]=1,i)
		c[i][j] = (c[i-1][j-1]+c[i-1][j])%MOD;
	inv[1] = jc[0] = inv[0] = 1;
	rep(i,2,n) inv[i] = int(llong(MOD-MOD/i)*inv[MOD%i]%MOD);
	rep(i,invc[0][0]=1,n){
		inv[i] = int(llong(inv[i-1])*inv[i]%MOD);
		jc[i] = int(llong(jc[i-1])*i%MOD);
		rep(j,0,i) invc[i][j] = int(
			llong(jc[i-j])*jc[j]%MOD*inv[i]%MOD);
	}
	memset(head+1,-1,n<<2);
	for(int a,b,i=1; i!=n; ++i){
		a = readint(), b = readint();
		addEdge(a,b), addEdge(b,a);
	}
	scan(rt,0); rep(i,1,n) id[i] = i;
	sort(id+1,id+n+1,cmp); rep(i,1,n){
		invdp[id[i]] = qkpow(dp[id[i]],MOD-2);
		for(int j=1; j!=i; ++j){
			const int &x = id[i], &y = id[j];
			if(fa[x] == fa[y]){ // brother
				lca[x][y] = lca[y][x] = fa[x];
				long long t = llong(dp[fa[x]])*invdp[x]%MOD
					*invdp[y]%MOD*invc[siz[fa[x]]-1][siz[x]]
					%MOD*invc[siz[fa[x]]-1-siz[x]][siz[y]]%MOD
					*c[siz[fa[x]]-1][siz[x]+siz[y]]%MOD;
				f[x][y] = f[y][x] = int(t); continue;
			}
			if(fa[x] == y || fa[y] == x) continue; // invalid
			const int us = siz[x]+siz[y];
			if(lca[fa[x]][y]){ // if x goes up
				const int p = fa[x], other = siz[p]-siz[x]-1;
				llong t = llong(dp[p])*invdp[x]%MOD*f[p][y]
					%MOD*invc[siz[p]-1][siz[x]]%MOD;
				f[x][y] = int(t*c[other+us][us]%MOD);
				lca[x][y] = lca[p][y]; // transfer
			}
			if(lca[x][fa[y]]){ // if y goes up
				const int p = fa[y], other = siz[p]-siz[y]-1;
				llong t = llong(dp[p])*invdp[y]%MOD*f[x][p]
					%MOD*invc[siz[p]-1][siz[y]]%MOD;
				f[x][y] = int((f[x][y]+t*c[other+us][us])%MOD);
				lca[x][y] = lca[x][p]; // inherit
			}
			f[y][x] = f[x][y], lca[y][x] = lca[x][y];
		}
	}
	rep(i,1,n) rep(j,1,n) cnt[i][j] += cnt[i][j-1];
	rep(i,1,n) rep(j,1,n) if(lca[i][j]){
		// assume i is chosen before j
		const llong v = llong(f[i][j])*c[siz[i]+siz[j]-1][siz[i]]
			%MOD*dp[i]%MOD*dp[j]%MOD*cnt[i][j-1]%MOD;
		val[lca[i][j]] = int((val[lca[i][j]]+v)%MOD);
	}
	solve(rt,0); printf("%lld\n",
		llong(val[rt])*qkpow(dp[rt],MOD-2)%MOD);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值