版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzelong2014/article/details/84797808
bzoj3451: Tyvj1953 Normal
分析
求随机写的点分治复杂度期望。
这类题有一个套路:考虑两个点的贡献。
如果说一个点
对另一个点
有贡献。那么说明
是
在分治树上的祖先。
也就是说
是
到
路径上的第一个被选择的点。
每个点成为第一个被选择的点的概率均等。
所以贡献是
然后实际上就是求所有树上路径为
的点对个数。
用点分治+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;
}