#33. 【UR #2】树上GCD

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

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

对于两个结点$u,v(u≠v)(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)=x (x≠0)$。

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

输入格式

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

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

输出格式

输出共$n−1$行,其中第$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 n≤2000 $pi$在${1,2,⋯,i−1}$中均匀随机
2 n≤100000
3 n≤200000
4 n≤200000 除根结点外的每个结点至多拥有一个孩子
5 n≤200000 $pi$在$max{i−10,1}$到$i−1$之间均匀随机
6 n≤50000
7 n≤100000
8 n≤150000
9 n≤200000
10 n≤200000

时间限制: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\n",ans[i]+Ans[i]);
return 0;
}

【UOJ#33】【UR#2】树上GCD 有根树点分治 + 容斥原理 + 分块的更多相关文章

  1. UOJ33 [UR #2] 树上GCD 【点分治】【容斥原理】【分块】

    题目分析: 树上点对问题首先想到点分治.假设我们进行了点分治并递归地解决了子问题.现在我们合并问题. 我们需要找到所有经过当前重心$ c $的子树路径.第一种情况是LCA为当前重心$ c $.考虑以$ ...

  2. [UOJ UR #2]树上GCD

    来自FallDream的博客,未经允许,请勿转载,谢谢. 传送门 看完题目,一般人都能想到 容斥稳了 .这样我们只要统计有多少点对满足gcd是i的倍数. 考虑长链剖分,每次合并的时候,假设我已经求出轻 ...

  3. 【uoj33】 UR #2—树上GCD

    http://uoj.ac/problem/33 (题目链接) 题意 给出一棵${n}$个节点的有根树,${f_{u,v}=gcd(dis(u,lca(u,v)),dis(v,lca(u,v)))}$ ...

  4. 【UOJ#33】【UR #2】树上GCD(长链剖分,分块)

    [UOJ#33][UR #2]树上GCD(长链剖分,分块) 题面 UOJ 题解 首先不求恰好,改为求\(i\)的倍数的个数,最后容斥一下就可以解决了. 那么我们考虑枚举一个\(LCA\)位置,在其两棵 ...

  5. 【UR #2】树上GCD

    这道题是有根树点分治+烧脑的容斥+神奇的分块 因为是规定1为根,还要求LCA,所以我们不能像在无根树上那样随便浪了,必须规定父亲,并作特殊讨论 因为gcd并不好求,所以我们用容斥转化一下,求x为gcd ...

  6. 【BZOJ-3784】树上的路径 点分治 + ST + 堆

    3784: 树上的路径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 462  Solved: 153[Submit][Status][Discuss ...

  7. 【Luogu2664】树上游戏(点分治)

    [Luogu2664]树上游戏(点分治) 题面 洛谷 题解 很好的一道点分治题. 首先直接点分治,考虑过每个分治重心的链的贡献. 我们从分治重心开始找每种颜色,强制令一种颜色只在其到分治重心的链上第一 ...

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

    题目大意:给定一棵有根树,边长均为1,对于每一个i,求树上有多少个点对,他们到lca距离的gcd是i.(n<=200,000) 做法:先容斥,求出gcd是i的倍数的点对,考虑长链剖分后从小到大合 ...

  9. UOJ#33. 【UR #2】树上GCD 点分治 莫比乌斯反演

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ33.html 题解 首先我们把问题转化成处理一个数组 ans ,其中 ans[i] 表示 d(u,a) 和 ...

随机推荐

  1. IOS之推送通知(本地推送和远程推送)

    推送通知和NSNotification是有区别的: NSNotification:是看不到的 推送通知:是可以看到的 IOS中提供了两种推送通知 本地推送通知:(Local Notification) ...

  2. Centos6.2 下 vncserver 的安装

    好久没用vnc了, 把今天装的过程记录一下, 这是一个从网上下载的标准Centos6.2 虚机镜像, 已经带了桌面. 默认的用户是root和tom, 口令都是tomtom. 因为ssh服务没起来, 简 ...

  3. Java核心技术点之注解

    本博文是对Java中注解相关知识点的简单总结,若有叙述不清晰或是不准确的地方,希望大家可以指正,谢谢大家:) 一.什么是注解 我们大家都知道Java代码中使用注释是为了向以后阅读这份代码的人解释说明一 ...

  4. java多线程系类:基础篇:09之interrupt()和线程终止方式

    概要 本章,会对线程的interrupt()中断和终止方式进行介绍.涉及到的内容包括:1. interrupt()说明2. 终止线程的方式2.1 终止处于"阻塞状态"的线程2.2 ...

  5. ASP.NET MVC验证 - 自定义验证规则、验证2个属性值不等【待验证】

    提示:保存后才提示错误信息 自定义验证特性,继承ValidationAttribute并实现IClientValidatable 这次重写了基类的IsValid()方法的另外一个重载,因为该重载包含了 ...

  6. 【转】mysql触发器的实战(触发器执行失败,sql会回滚吗)

    1   引言Mysql的触发器和存储过程一样,都是嵌入到mysql的一段程序.触发器是mysql5新增的功能,目前线上凤巢系统.北斗系统以及哥伦布系统使用的数据库均是mysql5.0.45版本,很多程 ...

  7. QT 常用控件一

    QWidget 创建窗口 如果widget未使用腹肌进行创建,则在显示时视为窗口或顶层widget. 由于顶层widget没有父级对象类来确保在其不再使用时删除,所以需要开发人员在应用程序中对其进程跟 ...

  8. Java中primitive type的线程安全性

    Java中primite type,如char,integer,bool之类的,它们的读写操作都是atomic的,但是有几个例外: long和double类型不是atomic的,因为long和doub ...

  9. Javascript将构造函数扩展为简单工厂

    一般而言,在Javascript中创建对象时需要使用关键字new(按构造函数去调用),但是某些时候,开发者希望无论new关键字有没有被显式使用,构造函数都可以被正常调用,即构造函数同时还具备简单工厂的 ...

  10. closure!

    总结一下闭包. 闭包的定义:当一个内部函数被其外部函数之外的变量所引用时,就形成了一个闭包. 一个最简单的闭包: function A(){ var count=0; return function( ...