题解 [Tyvj1953] Normal
又是神仙题
想到了期望的线性性拆开但拆开之后不会算
dalao题解
于是这题还真是个点分治
考虑如何期望的线性性
发现每个点的贡献就是这个点在最后形成的点分树中的期望深度
于是枚举每个点,计算这个点成为当前点的祖先的概率
注意到对于每个点对 \((i, j)\),\(i\) 成为 \(j\) 的祖先的条件是 \(i\) 是 \(i, j\) 形成的链上第一个选的点
而这个概率是 \(\frac{1}{dis(i, j)+1}\)
于是问题变为了统计每种距离的出现次数
不难想到点分治+FFT
但有个细节问题:如何保证复杂度呢?
如果每个点的每个子树都是与前面子树的前缀和进行合并,那复杂度是不对的,上面博客也给出了hack
于是现在需要让每次合并严格只与当前子树大小有关
考虑对所有子树中的所有点做一次,再减掉来自同一个子树的贡献
于是最终复杂度 \(O(nlog^2n)\)
点击查看代码
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
ll cnt[N];
double ans;
bool del[N];
const double pi=acos(-1.0);
int head[N], siz[N], msiz[N], rev[N], rot, size, top, bln, bct;
struct edge{int to, next;}e[N<<1];
inline void add(int s, int t) {e[++size]={t, head[s]}; head[s]=size;}
struct complex{
double x, y;
complex(double a=0, double b=0):x(a),y(b){}
inline complex operator + (complex b) {return complex(x+b.x, y+b.y);}
inline complex operator - (complex b) {return complex(x-b.x, y-b.y);}
inline complex operator * (complex b) {return complex(x*b.x-y*b.y, x*b.y+y*b.x);}
}f[N];
void fft(complex* a, int len, int op) {
for (int i=0; i<len; ++i) if (i<rev[i]) swap(a[i], a[rev[i]]);
complex w, wn, t;
for (int i=1; i<len; i<<=1) {
wn=complex(cos(pi/i), op*sin(pi/i));
for (int j=0,step=i<<1; j<len; j+=step) {
w=complex(1, 0);
for (int k=j; k<j+i; ++k,w=w*wn) {
t=w*a[k+i];
a[k+i]=a[k]-t;
a[k]=a[k]+t;
}
}
}
}
void fft(int op) {
for (bln=1,bct=0; bln<=top*2; bln<<=1,++bct) ;
while (top<=bln) f[top++]=complex(0, 0);
for (int i=0; i<bln; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bct-1));
fft(f, bln, 1);
for (int i=0; i<bln; ++i) f[i]=f[i]*f[i];
fft(f, bln, -1);
for (int i=0; i<bln; ++i) cnt[i]+=op*(ll)(f[i].x/bln+0.5);
}
void getrt(int u, int fa, int tot) {
siz[u]=1; msiz[u]=1;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (v==fa || del[v]) continue;
getrt(v, u, tot);
siz[u]+=siz[v];
msiz[u]=max(msiz[u], siz[v]);
}
msiz[u]=max(msiz[u], tot-msiz[u]);
if (msiz[u]<msiz[rot]) rot=u;
}
void getdis(int u, int fa, int dis) {
while (top<=dis) f[top++]=complex(0, 0);
++f[dis].x;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (v==fa || del[v]) continue;
getdis(v, u, dis+1);
}
}
void calc(int u) {
top=0;
getdis(u, 0, 0);
fft(1);
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (del[v]) continue;
top=0;
getdis(v, u, 1);
fft(-1);
}
}
void solve(int u) {
del[u]=1;
calc(u);
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (del[v]) continue;
rot=0;
getrt(v, u, siz[v]);
solve(rot);
}
}
signed main()
{
n=read();
memset(head, -1, sizeof(head));
for (int i=1,u,v; i<n; ++i) {
u=read()+1; v=read()+1;
add(u, v); add(v, u);
}
msiz[0]=n;
getrt(1, 0, n);
solve(rot);
for (int i=0; i<=n; ++i) ans+=1.0*cnt[i]/(i+1);
// cout<<"cnt: "; for (int i=0; i<=n; ++i) cout<<cnt[i]<<' '; cout<<endl;
printf("%.4lf\n", ans);
return 0;
}