zoukankan      html  css  js  c++  java
  • 【BZOJ 3451】Tyvj1953 Normal 思维题+期望概率+FFT+点分治

    我感觉是很强的一道题……
    即使我在刷专题,即使我知道这题是fft+点分治,我仍然做不出来……
    可能是知道是fft+点分治限制了我的思路???(别做梦了,再怎样也想不出来的……)
    我做这道题的话,一看就想单独算每个点的贡献,一开始想算每个点深度的期望,后来又想算每个点的点分树子树大小的期望,再后来就想利用点分治,于是就想算每个联通块的贡献,后来就想怂了……
    开始说这道题的做法……
    我们不算每个点的贡献,算每个点对的贡献!!!
    我们想啊,显然,每个点的点分树子树大小的期望加和就是答案,然而呢,对于一个点,其点分树子树大小的期望就是其他点存在于其点分树子树内的概率加和,这样我们算出每个点对(i,j)i存在于j的子树的概率然后加和就好了!!!
    然而怎么快速计算每个点对(i,j)i存在于j的子树的概率呢?我们仔细想一想什么情况下在点分树中j是i的祖先,就是在i到j的路径上的所有点中,j是第一个被删除的点,那么这个概率是多少呢?是1/(dis(i,j)+1),为什么呢?因为在i到j的路径上的所有点中,每个点是第一个被删除的概率是相等的(仔细想一想就会发现是这样的).
    现在问题好办了,我们只要统计出每一种距离的点对个数就好了,这是一个很简单的点分治的问题,处理的时候要用fft优化一下.
    感觉这道题里把n变成n^2的思想,或者说统计点对的思想,是很好很优秀的.
    这道题给我的一个很大的启发就是——思维是主体,算法是工具.

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <complex>
    #include <algorithm>
    typedef double db;
    typedef std::complex<db> cd;
    const int N=30010,Inf=0x3f3f3f3f;
    const db Pai=acos(-1.);
    int dis[N<<1],num[N<<1],cnt,deep[N<<1],max_deep,tmp[N<<1];
    int max,root,size[N];
    bool vis[N];
    int rev[N<<1],len;
    cd A[N<<1],B[N<<1];
    inline void fft(cd *C,int opt){
      register int i,j,k;cd temp;
      for(i=1;i<len;++i)if(rev[i]>i)std::swap(C[i],C[rev[i]]);
      for(k=2;k<=len;k<<=1){
        cd wn(cos(2*opt*Pai/k),sin(2*opt*Pai/k));
        for(i=0;i<len;i+=k){
          cd w(1.,0.);
          for(j=0;j<(k>>1);++j,w*=wn){
            temp=w*C[i+j+(k>>1)];
            C[i+j+(k>>1)]=(C[i+j]-temp);
            C[i+j]+=temp;
          }
        }
      }
    }
    inline void mul(int *a,int *b,int *c,int n){
      len=1;while(len<n)len<<=1;int i;
      for(i=1;i<len;++i)rev[i]=(rev[i>>1]>>1)|((i&1)?(len>>1):0);
      for(i=0;i<len;++i)A[i]=a[i],B[i]=b[i];
      fft(A,1),fft(B,1);
      for(i=0;i<len;++i)A[i]*=B[i];
      fft(A,-1);db inv=1./len;
      for(i=0;i<len;++i)c[i]=round(A[i].real()*inv);
    }
    struct V{
      int to,next;
    }c[N<<1];
    int head[N],t;
    inline void add(int x,int y){
      c[++t].to=y,c[t].next=head[x],head[x]=t;
    }
    int n;
    inline void dfs1(int x,int fa,int sum){
      int temp=0;size[x]=1;
      for(int i=head[x];i;i=c[i].next)
        if(c[i].to!=fa&&!vis[c[i].to]){
          dfs1(c[i].to,x,sum);
          size[x]+=size[c[i].to];
          temp=std::max(temp,size[c[i].to]);
        }
      temp=std::max(temp,sum-size[x]);
      if(temp<=max)max=temp,root=x;
    }
    inline void dfs2(int x,int fa,int d){
      ++deep[d],max_deep=std::max(max_deep,d);
      for(int i=head[x];i;i=c[i].next)
        if(c[i].to!=fa&&!vis[c[i].to])
          dfs2(c[i].to,x,d+1);
    }
    inline void dfs(int x,int sum){
      max=Inf,root=0,dfs1(x,0,sum);
      int i,j;num[0]=1,vis[root]=true;
      for(i=head[root];i;i=c[i].next)
        if(!vis[c[i].to]){
          dfs2(c[i].to,0,1);
          for(j=1;j<=max_deep;++j)
            num[j]+=deep[j],tmp[j]=deep[j],deep[j]=0;
          cnt=std::max(cnt,max_deep);
          mul(tmp,tmp,tmp,max_deep+max_deep+1);
          for(j=1;j<=(max_deep<<1);++j)
            dis[j]-=tmp[j],tmp[j]=0;
          max_deep=0;
        }
      for(i=0;i<=cnt;++i)
        tmp[i]=num[i],num[i]=0;
      mul(tmp,tmp,tmp,cnt+cnt+1);
      for(i=0;i<=(cnt<<1);++i)
        dis[i]+=tmp[i],tmp[i]=0;
      cnt=0;
      for(i=head[x=root];i;i=c[i].next)
        if(!vis[c[i].to])
          dfs(c[i].to,size[c[i].to]<size[x]?size[c[i].to]:sum-size[x]);
    }
    int main(){
      scanf("%d",&n);
      int i,x,y;
      for(i=1;i<n;++i){
        scanf("%d%d",&x,&y);
        ++x,++y,add(x,y),add(y,x);
      }
      dfs(1,n);
      db ans=0.;
      for(i=0;i<n;++i)
        ans+=(db)dis[i]/(db)(i+1);
      printf("%.4f
    ",ans);
      return 0;
    }
  • 相关阅读:
    SqlServer查询优化方法
    关于导入excel问题
    修改SQL数据库中表字段类型时,报“一个或多个对象访问此列”错误的解决方法
    软件架构之我见
    算法-插入排序
    算法-快速排序
    WCF系列 Restful WCF
    WCF系列 基础概念
    cocos2dx-是男人就坚持20s 练手项目
    nodejs 聊天室简单实现
  • 原文地址:https://www.cnblogs.com/TSHugh/p/8485913.html
Copyright © 2011-2022 走看看