这题分三步:葺网(期望)、淀粉质(点分治)、蓉翅(容斥),再佐以芬芳团(FFT),一道巨难无比的 luogu 黑题就诞生了。
期望
先考虑在淀粉树上,\(i\) 点在 \(j\) 点的子树里的概率。实际上这个问题的每种情况相当于是 \(n\) 个点的各种排列方式。这也就相当于,我们在选择 \(j\) 点之前,没有选择路径 \((i,j)\) 上的其他点,那么 \(j\) 的子树内就会包含 \(i\),否则不会。那么 \(i\) 点在 \(j\) 点子树里的概率就是 \(\dfrac 1{dis(i,j)+1}\)。注意,这里的 \(dis(i,j)\) 是边的数量,而不是点的数量。
由于期望具有可加性,所以相当于要求 \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\dfrac 1{dis(i,j)+1}\)。这玩意可以直接转化为求解 \(num_k=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[dis(i,j)=k]\)。
点分治
此时想到点分治其实水到渠成,关键问题是如何求解。
实际上合并儿子信息的本质就是多项式乘法(因为相当于是两个儿子的信息配对),自然想到 FFT,但是直接把所有都乘到一起正确性会假,用统计“除 \(x\) 子树以外其他子树的信息和”的思想时间复杂度会炸,所以都不行。
容斥
注意力惊人的注意到可以容斥。我们将答案分成两个部分:所有情况(目前考虑的树内的任意两个点可以配对)和错误情况(两个点必须在同一个儿子的子树内),二者相减即为答案。实际上这两个东西都是子树内所有点的深度所得的多项式直接平方的结果。
这样就可以保证在 \(O(m\log m)\) 的时间复杂度内完成合并,加上点分治,时间复杂度为 \(O(n\log^2n)\)。
#include<bits/stdc++.h>
#define stp(x) fixed<<setprecision(x)
using namespace std;
const int N=1e5+5;
const long double pi=acos(-1);
namespace FFT{
struct comn{long double a,b;};
struct dft{vector<comn>fg;};
int rev[N],k,mx=1;
comn operator+(comn x,comn y){
return {x.a+y.a,x.b+y.b};
}comn operator-(comn x,comn y){
return {x.a-y.a,x.b-y.b};
}comn operator*(comn x,comn y){
return {x.a*y.a-x.b*y.b,x.b*y.a+x.a*y.b};
}void operator+=(comn &x,comn y){x=x+y;}
void operator-=(comn &x,comn y){x=x-y;}
void operator*=(comn &x,comn y){x=x*y;}
void init(int n){
k=0,mx=1;
while(mx<=n) mx*=2,k++;
for(int i=0;i<mx;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
}void fft(dft &a,int n,int fl){
for(int i=0;i<n;i++)
if(i<rev[i]) swap(a.fg[i],a.fg[rev[i]]);
comn om={cos(pi),fl*sin(pi)},w={1,0};
for(int i=1;i<n;i*=2,om={cos(pi/i),fl*sin(pi/i)})
for(int j=0;j<n;j+=i*2,w={1,0})
for(int l=j;l<j+i;l++){
comn x=a.fg[l],y=w*a.fg[l+i];
a.fg[l]+=y,a.fg[l+i]=x-y,w*=om;
}
}void sat(dft &x,int len){
while(x.fg.size()<len) x.fg.push_back({0,0});
}void operator+=(dft &x,dft &y){
sat(x,y.fg.size());
for(int i=0;i<y.fg.size();i++) x.fg[i]+=y.fg[i];
}void operator-=(dft &x,dft &y){
for(int i=0;i<y.fg.size();i++) x.fg[i]-=y.fg[i];
}void pow2(dft &x){
int n=x.fg.size();rev[0]=0;
init(n+n),sat(x,mx),fft(x,mx,1);
for(int i=0;i<mx;i++) x.fg[i]*=x.fg[i];
fft(x,mx,-1);
for(int i=0;i<mx;i++) x.fg[i].a/=mx;
}
}using namespace FFT;
int n,dep[N],vis[N],sz[N],num[N];
long double ans;dft sum,c,d,al;vector<int>g[N];
void dfsrt(int x,int fa,int sm,int &rt){
sz[x]=1,num[x]=0;
for(auto y:g[x]){
if(y==fa||vis[y]) continue;
dfsrt(y,x,sm,rt),sz[x]+=sz[y];
num[x]=max(num[x],sz[y]),sz[y]=1;
}num[x]=max(num[x],sm-sz[x]);
if(num[x]<num[rt]) rt=x;
if(sz[x]==sm) sz[x]=1;
}void dfssz(int x,int fa){
for(auto y:g[x])
if(y!=fa&&!vis[y])
dfssz(y,x),sz[x]+=sz[y];
}void dfsp(int x,int fa,dft &id){
id.fg[dep[x]=dep[fa]+1].a++;
for(auto y:g[x])
if(y!=fa&&!vis[y]) dfsp(y,x,id);
}void solve(int x,int sm){
if(sm==1) return al.fg[0].a++,void();
d.fg.clear(),sat(d,sm);int rt=0;
sum.fg.clear(),sat(sum,sm),sum.fg[0].a=1;
dfsrt(x,0,sm,rt),dfssz(rt,0),vis[rt]=1,dep[rt]=0;
for(auto y:g[rt]) if(!vis[y]){
c.fg.clear(),sat(c,sz[y]+2);
dfsp(y,rt,c),sum+=c,pow2(c),d+=c;
}pow2(sum),sum-=d,al+=sum;
for(auto y:g[rt]) if(!vis[y]) solve(y,sz[y]);
}int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n,num[0]=1e9;
for(int i=1,x,y;i<n;i++){
cin>>x>>y,x++,y++;
g[x].push_back(y);
g[y].push_back(x);
}solve(1,n);
for(int i=0;i<n;i++)
ans+=al.fg[i].a/(i+1);
cout<<stp(4)<<ans;
return 0;
}//fast fourier transform
标签:sz,Normal,int,题解,void,BZOJ3451,num,comn,operator
From: https://www.cnblogs.com/chang-an-22-lyh/p/18678888/bzoj3451-normal-tj