UOJ UR#2树上GCD(缓存)

#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) {fa[v]=u; 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]; 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++) tim[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 x,i=1; i<=n-1; i++) x=read(),insert(x,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("%I64d\n",ans[i]+Ans[i]);
    return 0;
}
时间: 2024-10-13 08:07:49

UOJ UR#2树上GCD(缓存)的相关文章

[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比较小的时候常数较小.另外递归

【UR #2】树上GCD

这道题是有根树点分治+烧脑的容斥+神奇的分块 因为是规定1为根,还要求LCA,所以我们不能像在无根树上那样随便浪了,必须规定父亲,并作特殊讨论 因为gcd并不好求,所以我们用容斥转化一下,求x为gcd的因数的个数,这样就可以随便统计了,个人觉得代码比题解要好懂. 又因为统计完重心的所有子树,还有重心的父亲,所以在这个分支块内沿着重心的父亲一路向上爬,这时候重心的子树到重心的父亲的距离是变的,所以我们用神奇的分块大法,分类讨论,$<=\sqrt{n}$使用数组记录答案,方便以后再用到的时候统计,$

【UOJ】树上gcd

点分治 这道题还有很多种其它写法,什么长链剖分啦,启发式合并啦等等. 首先,我们可以把点对\((u,v)\)分成两类: 1.u到v的路径是一条链 2.u到v的路径不是一条链(废话) 对于第一类,显然\(f(u,v)\)就是链的长度,可以单独统计 对于第二类,就要在点分治上搞了 我们可以先计算出为d的倍数的点对数,最后容斥一下即可 在点分治中,我们取出当前子树的重心root,统计路径经过root的点对,那么又可以分成两类: A.u和v都在root的子树内 B.u和v一个在root的子树内,另一个不

uoj33 【UR #2】树上GCD

题目 大致是长剖+\(\rm dsu\ on\ tree\)的思想 先做一个转化,改为对于\(i\in[1,n-1]\)求出有多少个\(f(u,v)\)满足\(i|f(u,v)\),这样我们最后再做一个反演就好了 既然我们要求有多少对\(f(u,v)\)是\(i\)或\(i\)的倍数,我们需要在长剖的时候快速合并两边的信息,这个信息长得非常别致,形如到当前节点距离为\(i\)或\(i\)的倍数的节点个数 轻儿子这边还好说,我们直接暴力调和级数处理一下即可,但是这样的信息从中儿子哪里却非常不好继承

【做题】uoj#370滑稽树上滑稽果——巧妙dp

一个显然的结论是最终树的形态必然是一条链.具体证明只要考虑选定树上的某一条链,然后把其他部分全部接在它后面,这样答案一定不会变劣. 那么,一开始的想法是考虑每一位的最后出现位置,但这并不容易实现.注意到最终序列是单调递减的.我们在统计答案之前,把公共位先统计掉,即始终都是1的位.这样,剩下的位的最终结果都是0.这样,我们就避免了在统计时忽略某些数.那么,我们记dp[i]表示当前的结果为i的最小费用.我们在转移时枚举下一个数字是什么.这里无需担心数字的重复放置,因为它并不能让当前的数字发生变化.那

UOJ UR#9 App管理器

题目传送门 题目大意大概就是给你一个混合图(既有有向边又有无向边),对于每条无向边,u-v,问删去u->v,或删去v->u那条可以使新图强连通.(保证数据有解). 这道题前几个数据点送分. 枚举每一条边,先将它去掉,遍历图,如果可以达到所有点,即强连通. 比如去掉u->v,其实只要dfs(u),判断是否能到达v即可.可以直接删去,否则删另一条边,删去后因为图强连通,所以对之后的操作无影响. code: #include <cstdio> #include <cstrin

【UOJ 579】树上的颜色

[题目描述]: 给出一棵有N个点的有根树,结点编号为1,2,3,……,N,根结点编号为1,编号为i的结点涂上颜色Ci.现在有M个询问,每个询问要求求出以结点u为根的子树上涂有此种颜色的结点个数不小于k的颜色个数有多少. [输入描述]: 第一行包含两个正整数N和M. 第二行包含N个正整数,C1,C2,…,CN. 接下来的N-1行每行有两个正整数x和y,表示结点x和y有边相连. 再接下来的M行每行有两个正整数u和k,表示一个询问. [输出描述]: 输出M行,每行一个非负整数,对应每个询问的答案. [

开坑UR

会做几道算几道吧…… 开个坑比较能激励自己 强迫症buff+拖延症buff rating神马的?不要在意嘛 没写的都是坑 ~~~~~萌萌哒分割线~~~~~ UR#1 1 缩进优化 2 外星人 3 跳蚤国王下江南 UR#2 1 猪猪侠再战括号序列 2 跳蚤公路 3 树上GCD UR#3 1 核聚变反应强度 2 铀仓库 3 链式反应 UR#4 1 元旦三侠的游戏 只需目测就可以发现,我们可以table[b][a] 然后除了b=1的很长之外,其它都是sqrt(n)以下的 所以可以记忆化,然后b=1时把

【WC2013】糖果公园

UOJ 题解:树上莫队转移. 一开始UV打错,WA了一发. 1 #include<cmath> 2 #include<cstdio> 3 #include<iostream> 4 #include<vector> 5 #include<algorithm> 6 #define pb push_back 7 using namespace std; 8 inline char nc() { 9 static char b[1<<14],