• [UOJ]#33. 【UR #2】树上GCD


    题目大意:给定一棵有根树,边长均为1,对于每一个i,求树上有多少个点对,他们到lca距离的gcd是i。(n<=200,000)

    做法:先容斥,求出gcd是i的倍数的点对,考虑长链剖分后从小到大合并计算答案,小的部分先把每个深度的数量变为每个深度的倍数的数量,然后若深度>k,直接到大的里面暴力,若深度<=k,我们在大的里面维护f[i][j]表示深度mod i(1<=i<=k)为j的点数,理论上k取n^0.5时达到最小复杂度O(n^1.5),实际上k比较小的时候常数较小。另外递归计算的时候先递归轻儿子,这样始终都只要存一个f数组。

    代码:

    #include<cstdio>
    inline int read()
    {
        int x;char c;
        while((c=getchar())<'0'||c>'9');
        for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0';
        return x;
    }
    #define MN 200000
    #define K 20
    struct edge{int nx,t;}e[MN+5];
    int h[MN+5],en,d[MN+5],ht[MN+5],mx[MN+5],l[MN+5],cnt;
    int f[MN+5],c[MN+5],s[K+5][K+5];
    long long ans[MN+5],ss[MN+5];
    inline void ins(int x,int y){e[++en]=(edge){h[x],y};h[x]=en;}
    void dfs(int x)
    {
        l[x]=++cnt;
        if(mx[x])dfs(mx[x]);
        for(int i=h[x];i;i=e[i].nx)if(e[i].t!=mx[x])dfs(e[i].t);
    }
    void solve(int x,int v)
    {
        for(int i=h[x];i;i=e[i].nx)if(e[i].t!=mx[x])solve(e[i].t,1);
        if(mx[x])solve(mx[x],0);
        for(int i=h[x];i;i=e[i].nx)if(e[i].t!=mx[x])
        {
            for(int j=0;j<=ht[e[i].t];++j)
            {
                c[j]=f[l[e[i].t]+j];
                for(int k=j;(k+=j+1)<=ht[e[i].t];)f[l[e[i].t]+j]+=f[l[e[i].t]+k];
                if(j<K)ans[j+1]+=1LL*f[l[e[i].t]+j]*s[j+1][d[x]%(j+1)];
                else for(int k=0;(k+=j+1)<=ht[x];)ans[j+1]+=1LL*f[l[e[i].t]+j]*f[l[x]+k];
            }
            for(int j=0;j<=ht[e[i].t];++j)
            {
                f[l[x]+j+1]+=c[j];
                for(int k=1;k<=K;++k)s[k][(d[e[i].t]+j)%k]+=c[j];
            }
        }
        f[l[x]]=1;
        if(v)for(int i=1;i<=ht[x];++i)for(int k=1;k<=K;++k)s[k][(d[x]+i)%k]-=f[l[x]+i];
        else for(int k=1;k<=K;++k)++s[k][d[x]%k];
    }
    int main()
    {
        int n=read(),i,j;
        for(i=2;i<=n;++i)++ss[d[i]=d[j=read()]+1],ins(j,i);
        for(i=n;i;--i)for(j=h[i];j;j=e[j].nx)
            if(ht[e[j].t]+1>ht[i])ht[i]=ht[mx[i]=e[j].t]+1;
        dfs(1);solve(1,0);
        for(i=n;i;--i)for(ss[j=i]+=ss[i+1];(j+=i)<=n;)ans[i]-=ans[j];
        for(i=1;i<n;++i)printf("%lld
    ",ans[i]+ss[i]);
    }
  • 相关阅读:
    时隔三个月,不言谢归来了。学习播客继续更新日常所学。
    一个网易云音乐的外链URL简单的获取方法
    VisualStudioCode常用主题,与插件设置
    页面a标签统一跳转方法:base 标签
    Web前端开发规范(记录一):团队约定-基本原则
    JS中,正则表达式:match(/^(.*)(.)(.{1,8})$/)[3]分析
    Morgan模块使用
    Config模块使用
    MongoDB安装
    JSP-九大内置对象
  • 原文地址:https://www.cnblogs.com/ditoly/p/UOJ33.html
Copyright © 2020-2023  润新知