[BZOJ3451][Tyvj1953]Normal

题目大意

给定一棵 n 个节点的树,对这个树做点分治,但是每次我们不选择重心而是随机选点作为分治中心。定义每一个分治阶段对时间复杂度的贡献是分治区域的节点个数。
计算这个点分治的期望时间复杂度。

1n3×104

题目分析

首先显然可以看出,每个点对时间复杂度的是其在点分树上的深度。考虑对每个点分开计算贡献,现在相当于要求每个点在点分树上的期望深度之和。
定义

anc(x,y)={10xyotherwise

那么答案显然是 ni=1nj=1E[anc(i,j)] 。也就是所有点对前者为后者点分树祖先的概率之和。
考虑 x y 点分树祖先的充要条件: x x y 路径上第一个被选出来的点。因为选择路径外的点不会对这两个点的关系造成影响,而选择路径上的一个点,就会将这个路径上的所有点分在两棵不同的子树内。
所以答案其实就是 ni=1nj=11dis(i,j)
考虑用点分治加FFT统计出不同距离的点对的个数,最后扫一遍求答案就好了。
时间复杂度 O(nlog2n) .

代码实现

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

typedef long double db;

const db pi=acos(-1);
const int N=65600;
const int E=N<<1;

struct Z
{
    db x,y;

    inline Z(db x_=0.,db y_=0.){x=x_,y=y_;}

    inline Z operator+(Z const p)const{return Z(x+p.x,y+p.y);}
    inline Z operator-(Z const p)const{return Z(x-p.x,y-p.y);}
    inline Z operator*(Z const p)const{return Z(x*p.x-y*p.y,x*p.y+y*p.x);}
    inline Z operator/(db const k)const{return Z(x/k,y/k);}
}A[N],t[N],omega[N];

int last[N],fa[N],size[N],que[N],depth[N],dis[N],cnt[N],tmp[N],trs[N];
int tov[E],nxt[E];
bool vis[N];
int n,tot,head,tail,L;
db ans;

void insert(int x,int y){tov[++tot]=y,nxt[tot]=last[x],last[x]=tot;}

void DFT(Z *a,int len,int sig)
{
    for (int i=0;i<len;++i) t[trs[i]]=A[i];
    for (int l=2,half,p;l<=len;l<<=1)
    {
        half=l>>1,p=len/l;
        for (int i=0;i<half;++i)
        {
            Z w=omega[sig>0?p*i:len-p*i];
            for (int j=i;j<len;j+=l)
            {
                Z u=t[j],v=w*t[j+half];
                t[j]=u+v,t[j+half]=u-v;
            }
        }
    }
    for (int i=0;i<len;++i) A[i]=t[i];
}

void FFT(Z *a,int len)
{
    DFT(a,len,1);
    for (int i=0;i<len;++i) a[i]=a[i]*a[i];
    DFT(a,len,-1);
    for (int i=0;i<len;++i) a[i]=a[i]/len;
}

void pre(int len)
{
    for (L=1;L<=len;L<<=1);
    for (int i=0;i<L;++i)
    {
        int ret=0;
        for (int j=1,x=i;j<L;j<<=1,x>>=1) ret=ret<<1|(x&1);
        trs[i]=ret;
    }
    for (int i=0;i<=L;++i) omega[i]=Z(cos(2.*i*pi/L),sin(2.*i*pi/L));
}

int core(int og)
{
    int rets=n,ret,i,x,y,tmp;
    for (head=0,fa[que[tail=1]=og]=0,dis[og]=1;head<tail;)
        for (i=last[x=que[++head]],size[x]=1;i;i=nxt[i])
            if ((y=tov[i])!=fa[x]&&!vis[y]) fa[que[++tail]=y]=x,dis[y]=dis[x]+1;
    for (head=tail;head>1;--head) size[fa[que[head]]]+=size[que[head]];
    for (head=1;head<=tail;++head)
    {
        for (i=last[x=que[head]],tmp=size[og]-size[x];i;i=nxt[i])
            if ((y=tov[i])!=fa[x]&&!vis[y]) tmp=max(tmp,size[y]);
        if (rets>tmp) ret=x,rets=tmp;
    }
    return ret;
}

void solve(int x)
{
    int c=core(x),mxd=0;
    if (size[x]!=n)
    {
        for (int head=1;head<=tail;++head) mxd=max(mxd,dis[que[head]]);
        for (int i=0;i<=mxd;++i) tmp[i]=0;
        for (int head=1;head<=tail;++head) ++tmp[dis[que[head]]];
        pre(mxd*2);
        for (int i=0;i<L;++i) A[i]=Z((i<=mxd?tmp[i]:0),0);
        FFT(A,L);
        for (int i=0;i<=mxd*2;++i) cnt[i+1]-=(int)round(A[i].x);
        mxd=0;
    }
    for (depth[c]=0,head=0,fa[que[tail=1]=c]=0;head<tail;mxd=max(mxd,depth[x]))
        for (int i=last[x=que[++head]],y;i;i=nxt[i])
            if ((y=tov[i])!=fa[x]&&!vis[y]) fa[que[++tail]=y]=x,depth[y]=depth[x]+1;
    for (int i=0;i<=mxd;++i) tmp[i]=0;
    for (int head=1;head<=tail;++head) ++tmp[depth[que[head]]];
    pre(mxd*2);
    for (int i=0;i<L;++i) A[i]=Z((i<=mxd?tmp[i]:0),0);
    FFT(A,L);
    for (int i=0;i<=mxd*2;++i) cnt[i+1]+=(int)round(A[i].x);
    vis[c]=1;
    for (int i=last[c],y;i;i=nxt[i])
        if (!vis[y=tov[i]]) solve(y);
}

int main()
{
    freopen("normal.in","r",stdin),freopen("normal.out","w",stdout);
    n=read();
    for (int i=1,x,y;i<n;++i) x=read()+1,y=read()+1,insert(x,y),insert(y,x);
    solve(1);
    for (int i=1;i<=n;++i) ans+=cnt[i]*(1./i);
    printf("%.4lf\n",(double)ans);
    fclose(stdin),fclose(stdout);
    return 0;
}

你可能感兴趣的:(fft,OI,概率与期望,点分治)