BZOJ3451:Tyvj1953 Normal 树分治+期望+FFT
思路:
考虑当\(i\)为重心时,\(j\)在\(i\)的子树中会产生\(1\)的贡献.发生这种情况当且仅当\(i\)是原来的树中从\(i\)到\(j\)的路径上所有的点中第一个删除的点.而由于随机删除我们发现概率为:
\[p=\frac{1}{dist(i,j)+1}\]
由于贡献为\(1\),那么期望贡献就是\(1*p\).
也就是说我们要计算对于每个点为重心时,其余所有点对这个点的贡献.
我们可以暴力枚举,这样是\(O(n^2)\)的.
另一种思路是利用树分治,每次只处理经过重心的路径.
考虑合并两颗子树,暴力合并是\(O(n^2)\),我们利用fft优化求卷积可以做到\(O(nlogn)\).
因此总的时间复杂度为\(O(nlog^2{n})\).
(比较简略,具体看代码)
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
namespace Fio{
inline int getc(){
static const int L=1<<15;static char buf[L],*S=buf,*T=buf;
if(S==T){T=((S=buf)+fread(buf,1,L,stdin));if(S==T)return EOF;}
return*S++;
}
template<typename T>inline void Get(T&x){
int c;while(!isdigit(c=getc()));x=c-'0';
while(isdigit(c=getc()))x=(x<<1)+(x<<3)+c-'0';
}
template<typename T>inline void Get(T&x,T&y){
Get(x),Get(y);
}
}
#define INF 0x3f3f3f3f
#define N 30010
typedef double f2;
static const f2 pi=acos(-1);
struct Cpx{
f2 x,y;
Cpx(){}
Cpx(f2 _x,f2 _y):x(_x),y(_y){}
Cpx operator+(const Cpx&B)const{return Cpx(x+B.x,y+B.y);}
Cpx operator-(const Cpx&B)const{return Cpx(x-B.x,y-B.y);}
Cpx operator*(const f2&p)const{return Cpx(p*x,p*y);}
Cpx operator/(const f2&p)const{return Cpx(x/p,y/p);}
Cpx operator*(const Cpx&B)const{return Cpx(x*B.x-y*B.y,x*B.y+y*B.x);}
inline void operator+=(const Cpx&B){x+=B.x,y+=B.y;}
inline void operator-=(const Cpx&B){x-=B.x,y-=B.y;}
inline void operator*=(const f2&p){x*=p,y*=p;}
inline void operator/=(const f2&p){x/=p,y/=p;}
inline void operator*=(const Cpx&B){f2 _x=x*B.x-y*B.y,_y=x*B.y+y*B.x;x=_x,y=_y;}
inline void operator=(const f2&p){x=p,y=0;}
}A[32768],B[32768];
inline void fft(Cpx A[],int n,int rev){
register int i,j,k;for(i=k=0;i<n;++i){if(i<k)swap(A[i],A[k]);for(j=n>>1;(k^=j)<j;j>>=1);}
Cpx w,wn,t;
for(k=2;k<=n;k*=2)
for(wn=Cpx(cos(2*pi/k),rev*sin(2*pi/k)),i=0;i<n;i+=k)
for(w=1,j=0;j<k/2;++j,w*=wn)
t=w,t*=A[i+j+k/2],A[i+j+k/2]=A[i+j]-t,A[i+j]+=t;
if(rev==-1)for(i=0;i<n;++i)A[i]/=n;
}
int head[N],next[N<<1],end[N<<1];
inline void addedge(int a,int b){static int q=1;end[q]=b,next[q]=head[a],head[a]=q++;}
inline void make(int a,int b){addedge(a,b),addedge(b,a);}
struct Array{
int a[N],t[N];
inline int get(int x,int tclock){
if(t[x]!=tclock)return t[x]=tclock,a[x]=0;
return a[x];
}
inline void modify(int x,int add,int tclock){
if(t[x]!=tclock)t[x]=tclock,a[x]=0;
a[x]+=add;
}
}dep,now;int tclock,tclock2;
f2 res;
bool ban[N];
int siz[N];
int Minmaxsiz,center;
inline void Getcenter(int x,int fa,int Treesiz){
siz[x]=1;int Maxsiz=0;
for(int j=head[x];j;j=next[j])if(end[j]!=fa&&!ban[end[j]])
Getcenter(end[j],x,Treesiz),siz[x]+=siz[end[j]],Maxsiz=max(Maxsiz,siz[end[j]]);
if((Maxsiz=max(Maxsiz,Treesiz-siz[x]))<Minmaxsiz)Minmaxsiz=Maxsiz,center=x;
}
void dfs(int x,int fa,int d){
siz[x]=1;
res+=1/(d+1.0);
now.modify(d,1,tclock2);
for(int j=head[x];j;j=next[j])if(end[j]!=fa&&!ban[end[j]])
dfs(end[j],x,d+1),siz[x]+=siz[end[j]];
}
inline void calc(int x,int Treesiz){
if(Treesiz==1)return;
Minmaxsiz=INF,Getcenter(x,-1,Treesiz),ban[center]=1;
++tclock;
int presiz=0;
for(int j=head[center];j;j=next[j])if(!ban[end[j]]){
++tclock2,dfs(end[j],center,1);
int l;for(l=1;l<presiz+siz[end[j]]+2;l<<=1);
for(int i=0;i<l;++i)A[i]=Cpx(0,0),B[i]=Cpx(0,0);
for(int i=1;i<=presiz;++i)A[i]=Cpx(dep.get(i,tclock),0);
for(int i=1;i<=siz[end[j]];++i)B[i]=Cpx(now.get(i,tclock2),0);
fft(A,l,1),fft(B,l,1);
for(int i=0;i<l;++i)A[i]*=B[i];
fft(A,l,-1);
for(int i=1;i<=presiz+siz[end[j]];++i){int t=(int)(A[i].x+.5);res+=t/(i+1.0);}
for(int i=1;i<=siz[end[j]];++i)
dep.modify(i,now.get(i,tclock2),tclock);
presiz+=siz[end[j]];
}
for(int j=head[center];j;j=next[j])if(!ban[end[j]])calc(end[j],siz[end[j]]);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("tt.in","r",stdin);
#endif
int n;Fio::Get(n);
register int i,j;int a,b;for(i=1;i<n;++i)Fio::Get(a,b),make(++a,++b);
calc(1,n);
printf("%.4lf\n",res*2+n);
return 0;
}
评论 (0)