• [UOJ UR #2]树上GCD


    来自FallDream的博客,未经允许,请勿转载,谢谢。


    传送门

    看完题目,一般人都能想到 容斥稳了 。这样我们只要统计有多少点对满足gcd是i的倍数。

    考虑长链剖分,每次合并的时候,假设我已经求出轻儿子子树内每一个距离的点的数量,我们需要先对这个序列做一个变换,把每个数变成下标是它倍数的数的和。

    然后枚举轻儿子到这个点距离dis,这样答案加上现在这棵树内已经计算的部分中 到这个点的距离是dis的倍数的数的和。

    考虑分块,对于dis>=k的,暴力做。对于dis<=k的,我们顺便维护数组f[i][j],表示深度膜i等于j的数的数量。

    长链剖分的合并次数是O(n)的,所以这个算法的复杂度是根号级别的。

    然后玄学调参  我一路调着把块大小从根号那里调到了十几那里巨快  什么鬼 应该是数据问题吧

    #include<iostream>
    #include<cstdio>
    #define MN 200000
    #define MK 14
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    struct edge{int to,next;}e[MN+5];long long ans[MN+5];
    int n,head[MN+5],cnt=0,num[MK+5][MK+5],fa[MN+5],dep[MN+5],D[MN+5],mxdp[MN+5],mx[MN+5],s[MN+5],S[MN+5],*mem[MN+5];
    inline void ins(int f,int t){e[++cnt]=(edge){t,head[f]};head[f]=cnt;}
    void Pre(int x)
    {
        mxdp[x]=dep[x];mx[x]=0;
        for(int i=head[x];i;i=e[i].next)
        {
            Pre(e[i].to);
            if(mxdp[e[i].to]>mxdp[x]) mxdp[x]=mxdp[e[i].to],mx[x]=e[i].to;
        }
    }
    void Solve(int x,int flag)
    {
        for(int i=head[x];i;i=e[i].next)
            if(e[i].to!=mx[x]) Solve(e[i].to,0);    
        if(mx[x]) Solve(mx[x],1);
        for(int i=head[x];i;i=e[i].next)
            if(e[i].to!=mx[x])
            {
                for(int j=dep[e[i].to];j<=mxdp[e[i].to];++j) S[j-dep[e[i].to]+1]=mem[e[i].to][j-dep[e[i].to]];
                int len=mxdp[e[i].to]-dep[e[i].to]+1;
                for(int ii=1;ii<=len;++ii)
                    for(int j=ii<<1;j<=len;j+=ii) S[ii]+=S[j];
                for(int j=1;j<=len;++j)    
                    if(j<=MK) ans[j]+=1LL*S[j]*num[j][dep[x]%j];
                    else for(int k=j;k<=mxdp[x]-dep[x];k+=j) ans[j]+=1LL*S[j]*s[dep[x]+k];
                for(int j=dep[e[i].to];j<=mxdp[e[i].to];++j) 
                {
                    s[j]+=mem[e[i].to][j-dep[e[i].to]];
                    for(int k=1;k<=MK;++k) num[k][j%k]+=mem[e[i].to][j-dep[e[i].to]];
                } 
            }
        ++s[dep[x]];for(int j=1;j<=MK;++j) ++num[j][dep[x]%j];
        if(!flag)
        {
            mem[x]=new int[mxdp[x]-dep[x]+3];
            for(int j=dep[x];j<=mxdp[x];++j) 
            {
                mem[x][j-dep[x]]=s[j],s[j]=0;
                for(int k=1;k<=MK;++k) num[k][j%k]=0;
            }
        }
    }
    
    int main()
    {
        n=read();
        for(int i=2;i<=n;++i) ins(fa[i]=read(),i),++D[dep[i]=dep[fa[i]]+1];
        Pre(1);Solve(1,1);
        for(int i=n;i;D[i]+=D[i+1],--i)
            for(int j=i<<1;j<=n;j+=i) 
                ans[i]-=ans[j];
        for(int i=1;i<n;++i) printf("%lld
    ",ans[i]+D[i]); 
        return 0;
    }
  • 相关阅读:
    设计模式-----简单工厂模式
    LeetCode题解002:两数相加
    LeetCode题解001:两数之和
    异常处理类-Throwable源码详解
    Windows下 gcc/g++的安装与配置
    Windows10下安装解压版MySQL教程
    Windows下Django项目搭建流程
    Linux域名服务DNS
    Linux文件共享服务 FTP,NFS 和 Samba
    操作系统:进程的概念和与程序的区别
  • 原文地址:https://www.cnblogs.com/FallDream/p/uoj33.html
Copyright © 2020-2023  润新知