●Joyoi Normal
阿新 • • 發佈:2018-03-11
oid 隨機 gpo i++ tdi pos 代碼 1.0 def
那麽必然要求出所有點對間的距離,所以采用點分治+FFT的做法。
對於分治的每一個重心,把整個子樹dfs一遍,得到所有節點到重心的距離,
並得到一個數組A[i],表示子樹中到重心距離為i的點數為A[i]個
然後讓A數組自乘,做FFT,可以得到改子樹內任意兩個點經過重心的距離,
顯然對於在同一個兒子子樹裏的兩個點,也被統計了進去,所以再對重心的每個兒子做上述操作,然後減掉無效信息。
復雜度$O(Nlog^2N)$
題鏈:
http://www.joyoi.cn/problem/tyvj-1953
題解:
定義d(u,v)這個函數,滿足:
d(u,v)=1,當且僅當在點分樹中,u是v的祖先
d(u,v)=0,其它情況
對於一個確定的點分樹來說,
$\sum d(u,v)$就是答案。
但是現在點分樹未知,我們再來考慮這個函數的取值
d(u,v)=1時必須滿足u是v的祖先,
那麽由於現在是隨機的,所以對於u到v的這條鏈上的點,
u必須是第一個被隨機為重心的點才能使得在點分樹中,u是v祖先。
而這個概率是1/(dis(u,v)+1),
所以d(u,v)的期望取值就是1*1/(dis(u,v)+1)
即現在的問題變為求:$ANS=\sum 1/(dis(u,v)+1)$
那麽必然要求出所有點對間的距離,所以采用點分治+FFT的做法。
對於分治的每一個重心,把整個子樹dfs一遍,得到所有節點到重心的距離,
並得到一個數組A[i],表示子樹中到重心距離為i的點數為A[i]個
然後讓A數組自乘,做FFT,可以得到改子樹內任意兩個點經過重心的距離,
顯然對於在同一個兒子子樹裏的兩個點,也被統計了進去,所以再對重心的每個兒子做上述操作,然後減掉無效信息。
復雜度$O(Nlog^2N)$
代碼:
#include<bits/stdc++.h> #define MAXN 65540 using namespace std; const double Pi=acos(-1); double ANS; bool vis[MAXN]; int size[MAXN]; int N; struct Edge{ int ent; int to[MAXN*2],nxt[MAXN*2],head[MAXN]; Edge(){ent=2;} void Adde(int u,int v){ to[ent]=v; nxt[ent]=head[u]; head[u]=ent++; } }E; struct Cpx{ double R,I; Cpx(){} Cpx(double _R,double _I):R(_R),I(_I){} Cpx operator - () const{return Cpx(-R,-I);} Cpx operator + (const Cpx &rtm) const{return Cpx(R+rtm.R,I+rtm.I);} Cpx operator - (const Cpx &rtm) const{return *this+(-rtm);} Cpx operator * (const Cpx &rtm) const{return Cpx(R*rtm.R-I*rtm.I,R*rtm.I+I*rtm.R);} Cpx operator / (const double k) const{return Cpx(R/k,I/k);} }A[MAXN]; namespace FFT{ int order[MAXN]; int Init(int _n){ static int n,len; for(n=1,len=0;n<=2*_n;n<<=1) len++; for(int i=0;i<n;i++) order[i]=(order[i>>1]>>1)|((i&1)<<(len-1)); return n; } void Fft(Cpx *Y,int n,int sign){ for(int i=0;i<n;i++) if(i<order[i]) swap(Y[i],Y[order[i]]); for(int d=2;d<=n;d<<=1){ Cpx dw(cos(2*Pi/d),sin(sign*2*Pi/d)),w,tmp; for(int i=0;w=Cpx(1,0),i<n;i+=d) for(int k=i;k<i+d/2;w=w*dw,k++) tmp=w*Y[k+d/2],Y[k+d/2]=Y[k]-tmp,Y[k]=Y[k]+tmp; } if(sign==-1) for(int i=0;i<n;i++) Y[i]=Y[i]/n; } } void getroot(int u,int dad,int num,int &root,int &rootmax){ int umax=0; size[u]=0; for(int e=E.head[u];e;e=E.nxt[e]){ int v=E.to[e]; if(v==dad||vis[v]) continue; getroot(v,u,num,root,rootmax); size[u]+=size[v]; umax=max(umax,size[v]); } size[u]++; umax=max(umax,num-size[u]); if(rootmax>umax) root=u,rootmax=umax; } void calc(int s,int dep,int sign){ static queue<int> Q; static int dis[MAXN],reach[MAXN],tim,n; memset(A,0,sizeof(A)); Q.push(s); reach[s]=++tim; dis[s]=dep; A[dis[s]].R+=1; n=dis[s]; while(!Q.empty()){ int u=Q.front(); Q.pop(); for(int e=E.head[u];e;e=E.nxt[e]){ int v=E.to[e]; if(reach[v]==tim||vis[v]) continue; reach[v]=tim; dis[v]=dis[u]+1; n=max(n,dis[v]); A[dis[v]].R+=1; Q.push(v); } } n=FFT::Init(n); FFT::Fft(A,n,1); for(int i=0;i<n;i++) A[i]=A[i]*A[i]; FFT::Fft(A,n,-1); for(int i=1;i<n;i++) ANS+=sign*A[i].R*1.0/(i+1); ANS+=sign*A[0].R; } void solve(int u){ if(size[u]!=N) calc(u,1,-1); int root=u,rootmax=size[u]; getroot(u,0,size[u],root,rootmax); vis[root]=1; calc(root,0,1); for(int e=E.head[root];e;e=E.nxt[e]){ int v=E.to[e]; if(vis[v]) continue; if(size[v]>size[root]) size[v]=size[u]-size[root]; solve(v); } } int main(){ ios::sync_with_stdio(0); cin>>N; for(int i=1,u,v;i<N;i++){ cin>>u>>v; u++; v++; E.Adde(u,v); E.Adde(v,u); } size[1]=N; solve(1); cout<<fixed<<setprecision(4)<<ANS<<endl; return 0; }
●Joyoi Normal