专用于解决 “树形拓扑序序列” 问题。
在这种问题里,我们可以枚举两个节点计算其贡献,而且两个节点分别作为两棵树的根时的答案容易获得。
此时定义 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=1n−1∣pi−pi+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;
}