GYM104373H Permutation on Tree 题解

First Post:

Last Update:

我比 做法少了一步,十分可惜。

考虑直接枚举最终排列中相邻的两个点 ,怎么算贡献。

我们考虑如何造出一棵点数为 的新树,使得 被捆绑成一个点,同时满足之前的限制。

因为树的拓扑序个数是 ,我们只需关注这个过程中 的变化即可。

对于 以及 的祖先,它们的 只是减少了 ,这部分变化可以提前预处理,设为

接下来考虑 的这条链。我们尝试把 这两条链归并为一条新链,然后处理新链上的点 的变化。

那么设 表示 这条链所有归并方式的贡献系数之和。(不考虑 本身的 变化)

那么 可以从 转移,表示在新链末端放一个 ,那么此时, 在新链上的 会变成 (这里的 数组是原树的 ),所以转移系数是 。从 的转移是同理的。

那么一对 的贡献就是

注意特判 的儿子和 的兄弟的情况。

DP 初值和转移顺序不再赘述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 5,P = 1e9 + 7,Lg = 12;
inline void Plus(int &x,const int &y) { x += y;if(x >= P) x -= P;}
inline int qpow(int a,int b) { int res = 1;while(b) {if(b&1) res = 1ll * res * a % P;a = 1ll * a * a % P;b >>= 1;} return res;}
int n,rt;
vector<int> G[N];
int sze[N];
int fac[N],ifac[N],inv[N];
inline void init(int n) {
fac[0] = 1;
for(int i = 1;i <= n;i++) fac[i] = 1ll * fac[i - 1] * i % P;
inv[1] = 1;
for(int i = 2;i <= n;i++) inv[i] = 1ll * inv[P % i] * (P - P / i) % P;
ifac[0] = 1;
for(int i = 1;i <= n;i++) ifac[i] = 1ll * ifac[i - 1] * inv[i] % P;
}
inline int C(int n,int m) { if(n < 0 || m < 0 || n < m) return 0;return 1ll * fac[n] * ifac[m] % P * ifac[n - m] % P;}
int mdp[N];

int ST[Lg][N],lg[N],dfn[N],dep[N],fa[N];
void dfs0(int x,int f) {
sze[x] = 1;ST[0][dfn[x] = ++dfn[0]] = f;
dep[x] = dep[f] + 1;
for(auto y : G[x])
if(y != f) fa[y] = x,dfs0(y,x),sze[x] += sze[y];
}
int Min(int x,int y) { return dfn[x] < dfn[y] ? x : y;}
void init_lca() {
lg[0] = -1;dfs0(rt,0);
for(int i = 1;i <= n;i++) lg[i] = lg[i >> 1] + 1;
for(int j = 1;j < Lg;j++)
for(int i = 1;i + (1 << j) - 1 <= n;i++)
ST[j][i] = Min(ST[j - 1][i],ST[j - 1][i + (1 << j - 1)]);
}
void dfs1(int x) {
if(x == rt) mdp[x] = 1ll * sze[x] * inv[sze[x] - 1] % P;
else mdp[x] = 1ll * mdp[fa[x]] * sze[x] % P * inv[sze[x] - 1] % P;
for(auto y : G[x])
if(y != fa[x]) dfs1(y);
}
int lca(int x,int y) {
if(x == y) return x;
x = dfn[x];y = dfn[y];
if(x > y) swap(x,y);
int k = lg[y - (x++)];
return Min(ST[k][x],ST[k][y-(1<<k)+1]);
}
int dp[N][N];
int All; // 原树的拓扑序方案
int DP() {
int ans = 0;
static vector<pair<int,int> > Ps[N * 2];
for(int i = 1;i <= n;i++)
for(int j = i;j <= n;j++) dp[i][j] = dp[j][i] = (fa[i] == fa[j]);
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++) {
if(lca(i,j) == i || lca(i,j) == j || i == j) continue;
if(dp[i][j] && i < j) {
int coef = 1ll * All * mdp[lca(i,j)] % P;
coef = 1ll * coef * sze[i] % P * sze[j] % P * inv[sze[i] + sze[j] - 1] % P;
Plus(ans,1ll * (coef + coef) * (j - i) % P);
}
else Ps[dep[i] + dep[j]].emplace_back(i,j);
}
for(int d = 1;d <= 2 * n;++d)
for(auto it : Ps[d]) {
int x = it.first,y = it.second;
int fx = fa[x],fy = fa[y];
Plus(dp[x][y],1ll * dp[fx][y] * sze[fx] % P * inv[sze[fx] + sze[y] - 1] % P);
Plus(dp[x][y],1ll * dp[x][fy] * sze[fy] % P * inv[sze[fy] + sze[x] - 1] % P);
if(x >= y) continue;
int coef = 1ll * All * mdp[lca(x,y)] % P;
coef = 1ll * coef * sze[x] % P * sze[y] % P * inv[sze[x] + sze[y] - 1] % P;
coef = 1ll * coef * dp[x][y] % P;
Plus(ans,1ll * (coef + coef) * (y - x) % P);
}
return ans;
}
int main() {
cin >> n >> rt;
for(int i = 1,u,v;i < n;i++)
cin >> u >> v,G[u].push_back(v),G[v].push_back(u);
init(n);
init_lca();
dfs1(rt);
All = fac[n - 1];
for(int i = 1;i <= n;i++) All = 1ll * All * inv[sze[i]] % P;
int ans = 0;
for(int i = 1;i <= n;i++)
for(auto j : G[i]) if(j != fa[i]) {
int coef = 1ll * All * mdp[i] % P * sze[j] % P;
Plus(ans,1ll * coef * abs(j - i) % P);
}
Plus(ans,DP());
cout << ans << endl;
return 0;
}