Loading

[数学记录]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);
}
posted @ 2022-12-07 15:33  purplevine  阅读(17)  评论(0编辑  收藏  举报