zoukankan      html  css  js  c++  java
  • bzoj3451 Normal (点分治+FFT)

    根据期望的线性性,考虑每个点的点分树子树期望大小

    j 会给 i 贡献 1 的大小当且仅当 j 在 i 的子树内

    所以就是这个式子 ∑ijp[i][j]  p[i][j]表示 j 在 i 子树内的概率

    j 在 i 的点分树子树内,相当于(i,j)这个路径上,i 是第一个被选择的分治重心

    而这个概率就是 p[i][j] = 1/(dis(i,j)+1)

    统计出所有距离为 i 的点对数量,这个可以用点分治来求,统计的时候相当于在做卷积,可以用 fft 优化

    时间复杂度O(nlog^2n)

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<stack>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 30010;
    const int INF = 1e9+7;
    const double Pi = acos(-1.0);
    
    int n,m;
    int maxson,sum,rt;
    int ans[maxn];
    double res=0;
    
    int h[maxn],size;
    struct E{
        int to,next;
    }e[maxn<<1];
    void add(int u,int v){
        e[++size].to=v;
        e[size].next=h[u];
        h[u]=size;
    }
    
    int rev[maxn],lim=1,l=0;
    
    struct Complex{
        double x,y;
        Complex(double xx=0,double yy=0){ x=xx,y=yy; }
    }A[maxn],B[maxn];
    Complex operator + (Complex a,Complex b){ return Complex(a.x+b.x,a.y+b.y); }
    Complex operator - (Complex a,Complex b){ return Complex(a.x-b.x,a.y-b.y); }
    Complex operator * (Complex a,Complex b){ return Complex(a.x*b.x-a.y*b.y,a.y*b.x+a.x*b.y); }
    
    void fft(Complex *A,int type){
        for(int i=0;i<lim;i++) if(i<rev[i]) swap(A[i],A[rev[i]]);
        for(int i=1;i<lim;i<<=1){
            Complex Wn(cos(Pi/i),type*sin(Pi/i));
            for(int j=0,p=i<<1;j<lim;j+=p){
                Complex w(1,0);
                for(int k=0;k<i;k++,w=w*Wn){
                    Complex x=A[j+k],y=w*A[i+j+k];
                    A[j+k]=x+y;
                    A[i+j+k]=x-y;
                }
            }
        }
    //    if(type==-1) for(int i=0;i<lim;i++) A[i].x/=lim;
    }
    
    int vis[maxn];
    int sz[maxn],son[maxn];
    void getroot(int u,int par){
        sz[u]=1; son[u]=0;
        for(int i=h[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(v==par||vis[v]) continue;
            getroot(v,u);
            sz[u]+=sz[v];
            son[u]=max(son[u],sz[v]);
        }
        son[u]=max(son[u],sum-sz[u]);
        if(maxson>son[u]){
            maxson=son[u];
            rt=u;
        }
    }
    
    int t[maxn],s[maxn],tmp[maxn],mxd,md;
    
    void dfs(int u,int par,int dep){
        t[dep]++; md=max(md,dep);
        for(int i=h[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(vis[v]||v==par) continue;
            dfs(v,u,dep+1);
        }
    }
    
    void mul(int *a,int *b,int _n,int _m,int *c){
        lim=1,l=0;
        while(lim<=_n+_m) lim<<=1,l++;
        for(int i=0;i<lim;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
        for(int i=0;i<=_n;i++) A[i].x=1.0*a[i];
        for(int i=0;i<=_m;i++) B[i].x=1.0*b[i];
        
        fft(A,1); fft(B,1);
        for(int i=0;i<lim;i++) A[i]=A[i]*B[i];
        fft(A,-1);
        
        for(int i=0;i<=_n+_m;i++){ c[i]=(int)(A[i].x/lim+0.5); }
        for(int i=0;i<lim;i++) A[i]=B[i]=(Complex){0,0};
    }
    
    void divide(int u){
        vis[u]=1; int mxd=0;
        s[0]=1;
        for(int i=h[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(vis[v]) continue;
            md=0; dfs(v,u,1);
            
            mul(s,t,mxd,md,tmp);
    //        for(int j=0;j<=md;j++) printf("%d ",tmp[j]); printf("\n");
            for(int j=0;j<=mxd+md;j++) ans[j]+=tmp[j],tmp[j]=0;
            for(int j=0;j<=md;j++) s[j]+=t[j],t[j]=0; // 合并已经遍历过的子树中的信息
            mxd=max(mxd,md);
        }
    //    printf("zhixing\n");
        for(int i=0;i<=mxd;i++) s[i]=0;
        for(int i=h[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(vis[v]) continue;
            rt=0,sum=sz[v],maxson=INF;
            getroot(v,u);
            divide(rt);
        }
    }
    
    ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f;}
    
    int main(){
        memset(h,-1,sizeof(h));
        n=read();
        int u,v;
        for(int i=1;i<n;i++){
            u=read(),v=read();
            add(u,v),add(v,u);
        }
        
        sum=n;
        maxson=INF;
        rt=0;
        getroot(1,0);
    
        divide(rt);
        
    //    for(int i=1;i<=n;i++) printf("%d ",ans[i]); printf("\n"); 
        
        for(int i=1;i<=n;i++) res+=1.0*ans[i]/(i+1);
        res*=2; // 路径是相互的
        res+=n; // 自己对自己的贡献 
        printf("%.4lf\n",res);
    
        return 0;
    }
  • 相关阅读:
    JAVA笔记整理-数组
    JAVA笔记整理-流程控制-关键字 break、continue 、return的区别
    JAVA笔记整理-流程控制-循环
    Razor语法和Razor引擎大全
    MVC中几种常用ActionResult
    DataInputStream&DataOutputStream---操作基本类型值的流
    PipedOutputStream&PipedInputStream---管道流
    RandomAccessFile--随机访问文件
    ObjectOutputStream&ObjectInputStream--对象流
    SequenceInputStream--序列流
  • 原文地址:https://www.cnblogs.com/tuchen/p/10619204.html
Copyright © 2011-2022 走看看