[Tyvj 1953] bzoj3451 Normal [fft+点分治+概率期望]

Description:
每次点分治随机选重心,问期望复杂度。
Solution:
每次点分的复杂度可以看成和重心形成的点对数量,那么 u u 为重心时和v形成点对的概率是 1dis(u,v)+1 1 d i s ( u , v ) + 1 。意思是 u u v路径上之前没有点被选为重心,答案即为 ni=1nj=11dis(i,j)+1 ∑ i = 1 n ∑ j = 1 n 1 d i s ( i , j ) + 1
套用点分治,利用生成函数统计点对距离与数量即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const double pi = acos(-1);
struct cp {
    double x, y;
    cp() {}
    cp(double _x, double _y) : x(_x), y(_y) {}
    cp friend operator + (const cp &a, const cp &b) {
        return cp(a.x + b.x, a.y + b.y);
    }
    cp friend operator - (const cp &a, const cp &b) {
        return cp(a.x - b.x, a.y - b.y);
    }
    cp friend operator * (const cp &a, double b) {
        return cp(a.x * b, a.y * b);
    }
    cp friend operator * (const cp &a, const cp &b) {
        return cp(a.x * b.x - a.y * b.y, a.y * b.x + a.x * b.y);
    }
} a[maxn];
int n, N, len, rt;
double ans;
int vis[maxn], mx[maxn], sz[maxn], dep[maxn];
vector<int> G[maxn];
void fft(cp *a, int f) {
    for(int i = 0; i < n; ++i) {
        int t = 0;
        for(int j = 0; j < len; ++j) {
            if(i >> j & 1) {
                t |= 1 << (len - j - 1);    
            }
        }
        if(i < t) {
            swap(a[i], a[t]);
        }
    }
    for(int l = 2; l <= n; l <<= 1) {
        int m = l >> 1;
        cp w = cp(cos(pi / m), f * sin(pi / m));
        for(int i = 0; i < n; i += l) {
            cp t = cp(1, 0);
            for(int k = 0; k < m; ++k, t = t * w) {
                cp x = a[i + k], y = t * a[i + m + k];
                a[i + k] = x + y;
                a[i + m + k] = x - y;
            } 
        }
    }
    if(f == -1) 
    for(int i = 0; i < n; ++i) {
        a[i].x /= n;
    }
}
void dfs(int u, int last) {
    n = max(n, dep[u]);
    ++a[dep[u]].x;
    for(int i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if(v == last || vis[v]) {
            continue;
        }
        dep[v] = dep[u] + 1;
        dfs(v, u);
    }
}
int getsize(int u, int last) {
    int ret = 1;
    for(int i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if(v == last || vis[v]) {
            continue;
        }
        ret += getsize(v, u);
    }
    return ret;
}
void getroot(int u, int last, int S) {
    sz[u] = 1;
    mx[u] = 0;
    for(int i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if(vis[v] || v == last) {
            continue;
        }
        getroot(v, u, S);
        mx[u] = max(mx[u], sz[v]);
        sz[u] += sz[v];
    }
    mx[u] = max(mx[u], S - sz[u]);
    if(mx[u] < mx[rt]) {
        rt = u;
    }
}
void calc(int tmp, double f) {
    for(n = 1, len = 0; n <= 2 * tmp; n <<= 1){ 
        ++len;
    }   
    fft(a, 1);
    for(int i = 0; i < n; ++i) {
        a[i] = a[i] * a[i];
    }
    fft(a, -1);
    for(int i = 0; i < n; ++i) {
        ans += (double)f * a[i].x / (double)(i + 1);
    }
    for(int i = 0; i < n; ++i) {
        a[i] = cp(0, 0);
    }
    n = 0;
}
void solve(int u) {
    dep[u] = 0;
    dfs(u, 0);
    calc(n, 1);
    for(int i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if(vis[v]) {
            continue;
        }
        dep[v] = 1;
        dfs(v, 0);
        calc(n, -1);
    }
    for(int i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if(vis[v]) {
            continue;
        }
        rt = 0;
        getroot(v, 0, getsize(v, 0));
        vis[rt] = 1;
        solve(rt);
    }
}
int main() {
    scanf("%d", &N);
    for(int i = 1; i < N; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        ++u;
        ++v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    mx[0] = 0x3f3f3f3f;
    getroot(1, 0, N);
    vis[rt] = 1;
    solve(rt);
    printf("%.4f\n", ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值