• 【UOJ#33】【UR#2】树上GCD 有根树点分治 + 容斥原理 + 分块


    #33. 【UR #2】树上GCD

    有一棵$n$个结点的有根树$T$。结点编号为$1n$,其中根结点为$1$

    树上每条边的长度为$1$。我们用$d(x,y)$表示结点$x,y$在树上的距离,$LCA(x,y)$表示$x,y$的最近公共祖先(即树中最深的既是$v$的祖先也是$u$的祖先的结点)。

    对于两个结点$u,v(uv)(u≠v)$,令$a=LCA(u,v)$,定义$f(u,v)=gcd(d(u,a),d(v,a))$

    其中$gcd(x,y)$表示$x,y$的最大公约数,特别地,$gcd(0,x)=gcd(x,0)=(x0)$

    对于所有$i{1,2,,n1}$,求出有多少对$(u,v(u<v)$,满足$f(u,v)=i$

    输入格式

    输入第一行包含一个正整数$n$。保证$n2$。

    第$2$到$n$行中,第$i$行有一个正整数$pi$,表示结点$i$在树上的父亲是$pi$。保证$pi<i$

    输出格式

    输出共$n1$行,其中第$i$行表示对于$i$的答案。

    C/C++ 输入输出 long long 时请用 %lld。C++ 可以直接使用 cin/cout 输入输出。

    样例一

    input

    5
    1
    2
    2
    1
    
    

    output

    8
    2
    0
    0
    
    

    样例二

    input

    8
    1
    2
    3
    4
    1
    6
    6
    
    

    output

    16
    9
    2
    1
    0
    0
    0
    
    

    样例三

    见样例数据下载。

    限制与约定

    测试点编号n的规模备注
    1 n2000
    $pi$在${1,2,⋯,i−1}$中均匀随机
    2 n100000
    3 n200000
    4 n200000
    除根结点外的每个结点至多拥有一个孩子
    5 n200000
    $pi$在$max{i10,1}$到$i1$之间均匀随机
    6 n50000
    7 n100000
    8 n150000
    9 n200000
    10 n200000

    时间限制:1s

    空间限制:256MB

    下载

    样例数据下载

    Solution

    这题是真的神....官方题解:UOJ Round#2 题解

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    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; 
    }
    #define maxn 200010
    int n,m,fa[maxn],root,tmp,block,siz;
    struct EdgeNode{int next,to;}edge[maxn<<1];
    int head[maxn],cnt;
    void add(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
    void insert(int u,int v) {add(u,v); add(v,u);}
    int size[maxn],maxx[maxn]; bool visit[maxn];
    void Getroot(int now,int fa)
    {
        size[now]=1; maxx[now]=0;
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=fa && !visit[edge[i].to])
                {    
                    Getroot(edge[i].to,now);
                    size[now]+=size[edge[i].to];
                    maxx[now]=max(maxx[now],size[edge[i].to]);
                }
        maxx[now]=max(maxx[now],siz-size[now]);
        if (maxx[now]<maxx[root]) root=now;
    }
    int num[maxn],deep[maxn];
    void DFSdeep(int now,int fa)
    {
        num[deep[now]]++;
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=fa && !visit[edge[i].to])
                {
                    deep[edge[i].to]=deep[now]+1;
                    DFSdeep(edge[i].to,now);
                }
        if (deep[now]>tmp) tmp=deep[now];
    }
    long long S[maxn],Ans[maxn],ans[maxn],tim[maxn],Num[maxn],Tim[maxn],f[510][510];
    void GetAns(int now)
    {
        visit[now]=1;
        int maxd=0,Maxd=0;
        for (int i=head[now]; i; i=edge[i].next)
            if (!visit[edge[i].to] && edge[i].to!=fa[now])
                {
                    deep[edge[i].to]=1;
                    tmp=0; DFSdeep(edge[i].to,now);
                    if (tmp>maxd) maxd=tmp;
                    for (int j=1; j<=tmp; j++)
                        for (int k=j; k<=tmp; k+=j)
                            tim[j]+=num[k];
                    for (int j=1; j<=tmp; j++)
                        Num[j]+=num[j],Ans[j]+=num[j],S[j]+=tim[j]*Tim[j],
                        Tim[j]+=tim[j],tim[j]=0,num[j]=0;
                }
        Num[0]=1;
        int zz=0,ss=now;
        for (int i=fa[now]; !visit[i]&&i; ss=i,i=fa[i])
            {
                tmp=0;  zz++;
                for (int j=head[i]; j; j=edge[j].next)
                    if (edge[j].to!=fa[i] && !visit[edge[j].to] && edge[j].to!=ss)
                        deep[edge[j].to]=1,DFSdeep(edge[j].to,i);
                if (tmp>Maxd) Maxd=tmp;
                for (int j=1; j<=tmp; j++)
                    for (int k=j; k<=tmp; k+=j)
                        tim[j]+=num[k];
                int tt=tmp<block?tmp:block;
                for (int j=1; j<=tt; j++)
                    {
                        if (f[j][zz%j]==-1) 
                            {
                                f[j][zz%j]=0;
                                for (int k=(j-zz%j)%j; k<=maxd; k+=j)
                                    f[j][zz%j]+=Num[k];
                            }
                        S[j]+=f[j][zz%j]*tim[j];
                    }
                for (int j=block+1; j<=tmp; j++)
                    for (int k=(j-zz%j)%j; k<=maxd; k+=j)
                        S[j]+=Num[k]*tim[j];
                for (int j=1; j<=tmp; j++) tim[j]=0,num[j]=0;
                Ans[zz]++;
            }
        int l=1,r=0;
        long long tmpans=0;
        for (int i=2; i<=zz+maxd; i++)
            tmpans+=r+1<i?Num[++r]:0,tmpans-=l+zz<i?Num[l++]:0,Ans[i]+=tmpans;
        int tt=Maxd<block?Maxd:block;
        for (int i=1; i<=tt; i++)
             for (int j=0; j<=i-1; j++)
                 f[i][j]=-1;
        for (int i=0; i<=maxd; i++) Num[i]=0,Tim[i]=0;
        for (int i=head[now]; i; i=edge[i].next)
            if (!visit[edge[i].to])
                {
                    root=0;
                    siz=size[edge[i].to];
                    Getroot(edge[i].to,now);
                    GetAns(root);
                }
    }
    void Freopen() {freopen("TreeGCD.in","r",stdin);freopen("TreeGCD.out","w",stdout);}
    void Fclose() {fclose(stdin);fclose(stdout);}
    int main()
    {
        n=read(); block=(int)sqrt(n);
        for (int i=2; i<=n; i++) fa[i]=read(),insert(fa[i],i);
        maxx[root]=0x7fffffff; siz=n; memset(f,-1,sizeof(f));
        Getroot(1,0);
        GetAns(root);
        for (int i=n-1; i; i--)
            {
                ans[i]=S[i];
                   for (int j=i+i; j<=n-1; j+=i)
                       ans[i]-=ans[j];
            }
        for (int i=1; i<=n-1; i++) printf("%lld
    ",ans[i]+Ans[i]);
        return 0;
    }

     

  • 相关阅读:
    ES6-11学习笔记--正则表达式的扩展
    ES6-11学习笔记--字符串的扩展
    ES6-11学习笔记--Map
    ES6-11学习笔记--Set
    ES6-11学习笔记--Symbol
    final
    MySQL
    爬虫1
    laravel
    HTML学习
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5550481.html
Copyright © 2020-2023  润新知