• bzoj 3451 Normal


    Description

    某天WJMZBMR学习了一个神奇的算法:树的点分治!
    这个算法的核心是这样的:
    消耗时间=0
    Solve(树 a)
    消耗时间 += a 的 大小
    如果 a 中 只有 1 个点
    退出
    否则在a中选一个点x,在a中删除点x
    那么a变成了几个小一点的树,对每个小树递归调用Solve
    我们注意到的这个算法的时间复杂度跟选择的点x是密切相关的。
    如果x是树的重心,那么时间复杂度就是O(nlogn)
    但是由于WJMZBMR比较傻逼,他决定随机在a中选择一个点作为x!
    Sevenkplus告诉他这样做的最坏复杂度是O(n^2)
    但是WJMZBMR就是不信><。。。
    于是Sevenkplus花了几分钟写了一个程序证明了这一点。。。你也试试看吧^^
    现在给你一颗树,你能告诉WJMZBMR他的傻逼算法需要的期望消耗时间吗?(消耗时间按在Solve里面的那个为标准)
    题面

    Solution

    考虑一个点会被算入贡献几次,假如我们构出了一棵点分树,那么就贡献了 (dep)
    那么我们考虑任意一个点对 ((x,y)), 只有 (y) 在点分树上是 (x) 的父亲,才对 (x)(1) 的贡献
    并且 (y) 在点分树上是 (x) 的父亲的条件是 在原树中 ((x,y)) 这条路径上的任意一个点都没有在分治到 (y) 之前被分治到
    又因为一个点被随机的概率是均等的,则可以得出 (y)(x)(1) 的贡献的概率是 (frac{1}{dis(x,y)})

    所以原问题转化为求 (sum_{i=1}^{n}sum_{j=1}^nfrac{1}{dis(x,y)})
    这个可以用点分治实现
    (t[i]) 表示路径长度为 (i) 的点的数量
    (t[i]=sum_{j=1}^{i}t[i]*t[i-j])
    这是一个卷积的形式,可以用 (FFT) 优化
    复杂度 (O(n*log^2n))

    #include<bits/stdc++.h>
    using namespace std;
    typedef complex<double> dob;
    const int N=240005;const double pi=acos(-1.0);
    int n,nxt[N<<1],to[N<<1],num=0,head[N],rt;
    int son[N]={N},sz[N],sum;bool vis[N];
    inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
    inline void getroot(int x,int last){
    	sz[x]=1;son[x]=0;
    	for(int i=head[x];i;i=nxt[i]){
    		int u=to[i];if(u==last || vis[u])continue;
    		getroot(u,x);
    		sz[x]+=sz[u];son[x]=max(son[x],sz[u]);
    	}
    	son[x]=max(son[x],sum-sz[x]);
    	if(son[x]<son[rt])rt=x;
    }
    int st[N],top=0,t[N],s[N],h[N],dis[N];
    inline void dfs(int x,int last,int val){
    	s[val]++;st[++top]=x;dis[x]=val;
    	for(int i=head[x];i;i=nxt[i]){
    		int u=to[i];if(u==last || vis[u])continue;
    		dfs(u,x,val+1);
    	}
    }
    namespace FFT{
    	int n,L=0,R[N];
    	inline void FFT(dob *A,int o){
    		for(int i=0;i<n;i++)if(i<R[i])swap(A[i],A[R[i]]);
    		for(int i=1;i<n;i<<=1){
    			dob wn(cos(pi/i),sin(pi/i*o)),x,y;
    			for(int j=0;j<n;j+=(i<<1)){
    				dob w(1,0);
    				for(int k=0;k<i;k++,w*=wn){
    					x=A[j+k];y=w*A[j+k+i];
    					A[j+k]=x+y;A[j+k+i]=x-y;
    				}
    			}
    		}
    	}
    	inline void mul(dob *A,int len){
    		for(n=1,L=0;n<=len;n<<=1)L++;
    		for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    		FFT(A,1);
    		for(int i=0;i<=n;i++)A[i]*=A[i];
    		FFT(A,-1);
    		A[0]=0;for(int i=1;i<=n;i++)h[i]=(int)(A[i].real()/n+0.5),A[i]=0;
    	}
    }
    dob A[N];
    inline void calc(int x,int op,int val){
    	top=0;dfs(x,x,val+1);
    	int len=(sz[x]+1)<<1;
    	for(int i=1;i<=top;i++)A[dis[st[i]]]=s[dis[st[i]]];
    	FFT::mul(A,len);
    	for(int i=1;i<=len;i++)t[i]+=h[i+1]*op;
    	for(int i=1;i<=top;i++)s[dis[st[i]]]--;
    }
    inline void solve(int x){
    	vis[x]=1;getroot(x,x);calc(x,1,0);
    	for(int i=head[x];i;i=nxt[i]){
    		int u=to[i];if(vis[u])continue;
    		getroot(u,x);calc(u,-1,1);
    		rt=0;sum=sz[u];getroot(u,x);solve(rt);
    	}
    }
    int main(){
      freopen("pp.in","r",stdin);
      freopen("pp.out","w",stdout);
      int x,y;
      scanf("%d",&n);
      for(int i=1;i<n;i++){
    	  scanf("%d%d",&x,&y);x++;y++;
    	  link(x,y);link(y,x);
      }
      rt=0;sum=n;getroot(1,1);solve(rt);
      double ans=0;
      for(int i=1;i<=n;i++)ans+=1.0*t[i]/i;
      printf("%.4lf
    ",ans);
      return 0;
    }
    
    
    
  • 相关阅读:
    End of 2007, where are you?
    2007年,前进!
    PHP框架Yii快速搭建高并发网站
    需要 gmail 与 wallop 邀请的 请留言给 email
    开始关注Mono了
    百度,阿里巴巴,google
    重返 cnblogs.com
    解决VMware虚拟机桥接不能上网的问题
    SecureCRT
    linux的ssh和sshd配置
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8505927.html
Copyright © 2020-2023  润新知