bzoj3451: Tyvj1953 Normal点分治+FFT 概率与期望

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzelong2014/article/details/84797808

bzoj3451: Tyvj1953 Normal

题目传送门

分析

求随机写的点分治复杂度期望。
这类题有一个套路:考虑两个点的贡献。
如果说一个点 x x 对另一个点 y y 有贡献。那么说明 y y x x 在分治树上的祖先。
也就是说 y y x x y y 路径上的第一个被选择的点。
每个点成为第一个被选择的点的概率均等。
所以贡献是 1 d i s ( x , y ) + 1 \frac{1}{dis(x,y)+1}
然后实际上就是求所有树上路径为 x x 的点对个数。
用点分治+FFT解决。

代码

#include<bits/stdc++.h>
const int N = 1e5 + 10;
const double pi = acos(-1.0);
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
struct cp {
    double r, i;
    cp(double _r = 0, double _i = 0) : r(_r), i(_i) {}
    cp operator + (cp a) {return cp(r + a.r, i + a.i);}
    cp operator - (cp a) {return cp(r - a.r, i - a.i);}
    cp operator * (cp a) {return cp(r * a.r - i * a.i, r * a.i + i * a.r);}
}a[N], w[N];
int L, R[N], de[N], pr[N], to[N], nx[N], sz[N], ret[N], tp, sums, mn, mx, G; bool vis[N];
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
void adds(int u, int v) {add(u, v); add(v, u);}
void Pre(int m) {
    int x = 0; L = 1;
    for(;(L <<= 1) <= m; ++x) ;
    for(int i = 1;i < L; ++i) R[i] = R[i >> 1] >> 1 | (i & 1) << x;
    for(int i = 0;i < L; ++i) w[i] = cp(cos(2 * pi * i / L), sin(2 * pi * i / L));
}
void DFT(cp *F) {
    for(int i = 0;i < L; ++i) 
        if(i < R[i]) 
            std::swap(F[i], F[R[i]]);
    for(int i = 1, d = L >> 1; i < L; i <<= 1, d >>= 1)
        for(int j = 0;j < L; j += i << 1) {
            cp *l = F + j, *r = F + j + i, *p = w, tp;
            for(int k = 0;k < i; ++k, ++l, ++r, p += d)
                tp = *p * *r, *r = *l - tp, *l = *l + tp;
        }
}
void Get(int u, int fa) {
    ++a[de[u]].r; mx = std::max(mx, de[u]);
    for(int i = pr[u]; i; i = nx[i])
        if(to[i] != fa && !vis[to[i]])
            de[to[i]] = de[u] + 1, Get(to[i], u);
}
void FFT() {
    DFT(a);
    for(int i = 0;i < L; ++i) a[i] = a[i] * a[i];
    DFT(a);
}
void Calc(int u, int w, int p) {
    de[u] = w; mx = 0; Get(u, 0);
    mx <<= 1; Pre(mx); FFT();
    for(int i = 0;i <= mx; ++i) 
        ret[i] += p * (int)(a[L - i & L - 1].r / L + 0.5);
    for(int i = 0;i < L; ++i) a[i].r = a[i].i = 0;
}
void Rt(int u, int fa) {
    sz[u] = 1; int tp = 0;
    for(int i = pr[u]; i; i = nx[i])
        if(to[i] != fa && !vis[to[i]])
            Rt(to[i], u),
            sz[u] += sz[to[i]], 
            tp = std::max(tp, sz[to[i]]);
    tp = std::max(tp, sums - sz[u]);
    if(tp < mn) mn = tp, G = u;
}
void Work(int u, int psum) {
    vis[u] = true; Calc(u, 0, 1);
    for(int i = pr[u]; i; i = nx[i])
        if(!vis[to[i]]) {
            Calc(to[i], 1, -1);
            sums = sz[to[i]] > sz[u] ? psum - sz[to[i]] : sz[to[i]];
            mn = 1e9; Rt(to[i], u);
            Work(G, sums);
        }
}
int main() {
    int n = ri();
    for(int i = 1;i < n; ++i) adds(ri() + 1, ri() + 1);
    sums = n; mn = 1e9; Rt(1, 0); Work(G, n);
    double r = 0;
    for(int i = 0;i < n; ++i) r += ret[i] / (double)(i + 1);
    printf("%.4lf\n", r);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/84797808