[数学记录]CF1540B Tree Arrays
题意:
一棵树上的点编号 \(1 \to n\),初始随机一个点染色,每一步随机一个与已染色点有连边的点且未染色的点染色,求染色序列的点的编号序列的期望逆序对数。
\(n \leq 200\)
\(n\) 这么小,所以不妨以此钦定根来做。
\(n\) 还是这么小,不如枚举一对点 \(u,v\),计算其构成逆序对的概率。
容易发现,只用关心根到 \(u,v\) 的路程。其中也只用关心 lca 到两边的路程。
问题转化为:给定两个栈,每次随机弹出一个栈的栈顶,求某个栈先被弹空的概率。
这是明显的 dp 形式。设 \(dp_{i,j}\) 表示两个栈大小分别为 \(i,j\) 且 \(i\) 先空的概率,则 \(dp_{i,j} = \dfrac{1}{2}(dp_{i-1,j} + dp_{i,j-1})\)。
结束了。复杂度 \(O(n^3 \log n)\),最后的 \(\log\) 是求 lca 的复杂度。
感觉一步步转化其实都相当平凡,所以没想到可能只是自己没有用心去想。\(n\) 很小,这告诉我们可以去枚举相当多东西,减少相当多变量的,于是能想到直接把贡献下放到点对上。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M = 205, mod = 1000000007, invt = (mod + 1) / 2;
int qpow(int a, int b){
long long ans = 1ll;
for(; b; b >>= 1) {if(b & 1) ans = 1ll * ans * a % mod; a = 1ll * a * a % mod;}
return ans;
}
int add(int a, int b) {a += b; return a > mod ? a-mod : a;}
int minus(int a, int b) {a -= b; return a < 0 ? a+mod : a;}
void addn(int &x, int y) {x += y; if(x > mod) x -= mod;}
int f[M][M]; int dep[M];
struct edge{
int to, nxt;
}e[M << 1];
int head[M], cnt1;
void link(int u, int v){
e[++cnt1] = {v, head[u]}; head[u] = cnt1;
}
struct LCA{
int f[M][15], d[M];
LCA() {memset(f, 0, sizeof(f)); memset(d, 0, sizeof(d));}
void dfs(int u, int fa){
f[u][0] = fa; d[u] = d[fa] + 1;
for(int i = 1; i <= 10; i++) f[u][i] = f[f[u][i - 1]][i - 1];
for(int i = head[u]; i; i = e[i].nxt){
int v = e[i].to; if(v == fa) continue; dfs(v, u);
}
}
int lca(int u, int v){
if(d[u] < d[v]) swap(u, v);
for(int i = 10; i >= 0; i--) if(d[f[u][i]] >= d[v]) u = f[u][i];
if(u == v) return u;
for(int i = 10; i >= 0; i--) if(f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
return u == v ? u : f[u][0];
}
void init(int rt) {
memset(f, 0, sizeof(f)); memset(d, 0, sizeof(d));
d[rt] = 0; dfs(rt, 0);
}
int dis(int u, int f) {return abs(d[u] - d[f]);}
}t;
int n;
int main(){
scanf("%d", &n);
for(int i = 1; i < n; i++){
int u, v; scanf("%d %d", &u, &v);
link(u, v); link(v, u);
}
for(int i = 1; i <= n; i++) f[0][i] = 1;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
f[i][j] = 1ll * add(f[i-1][j], f[i][j-1]) * invt % mod;
// for(int i = 0; i <= n; i++) {
// for(int j = 0; j <= n; j++) printf("%d ", f[i][j]);
// printf("\n");
// }
int ans = 0;
for(int i = 1; i <= n; i++) {
t.init(i);
for(int u = 1; u <= n; u++) {
for(int v = 1; v < u; v++) {
if(u == v) continue;
int l = t.lca(u, v);
addn(ans, f[t.dis(u, l)][t.dis(v, l)]);
}
}
}
printf("%d\n", 1ll * ans * qpow(n, mod-2) % mod);
}
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/16963243.html