http://uoj.ac/problem/33 (题目链接)

题意

  给出一棵${n}$个节点的有根树,${f_{u,v}=gcd(dis(u,lca(u,v)),dis(v,lca(u,v)))}$,求对于${1<=i<=n-1,}$有多少${f_{u,v}=i}$。

Solution

  虽然有官方题解,但是感觉写的并不是很详细→_→,不过自己推敲推敲还是能懂的。而且这道题细节也很多,膜拜了DaD3zZ大爷的代码完全弄懂。。

  具体的一些实现细节就看看代码吧。(本来想详细的写写的,然而语文太差了,写了一半感觉发出来估计没什么人看得懂→_→)

  ${ans[i]}$表示公约数为${i}$的点对数目,${ANS[i]}$表示最大公约数为${i}$的点对数目。

细节

  竟然1A了w(゚Д゚)w

代码

// uoj33
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf 100000000
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std; const int maxn=200010;
int head[maxn],f[maxn],size[maxn],deep[maxn],vis[maxn],fa[maxn];
int Dargen,n,m,sum,cnt,maxd,maxD,block;
LL NUM[maxn],num[maxn],Cnts[maxn],cnts[maxn],ans[maxn],ANS[maxn],F[500][500]; struct edge {int to,next;}e[maxn<<1]; void link(int u,int v) {
e[++cnt]=(edge){v,head[u]};head[u]=cnt;
e[++cnt]=(edge){u,head[v]};head[v]=cnt;
}
void caldargen(int x,int fa) {
f[x]=0;size[x]=1;
for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) {
caldargen(e[i].to,x);
size[x]+=size[e[i].to];
f[x]=max(f[x],size[e[i].to]);
}
f[x]=max(f[x],sum-size[x]);
if (f[x]<f[Dargen]) Dargen=x;
}
void caldeep(int x,int fa) {
cnts[deep[x]]++;
for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) {
deep[e[i].to]=deep[x]+1;
caldeep(e[i].to,x);
}
}
void work(int x) {
vis[x]=1;maxd=maxD=0;
for (int d,i=head[x];i;i=e[i].next) if (e[i].to!=fa[x] && !vis[e[i].to]) {
deep[e[i].to]=1;caldeep(e[i].to,x);
for (d=0;cnts[d+1];d++);
maxd=max(maxd,d);
for (int j=1;j<=d;j++)
for (int k=j;k<=d;k+=j) num[j]+=cnts[k];
for (int j=1;j<=d;j++) ans[j]+=num[j]*NUM[j],ANS[j]+=cnts[j];
for (int j=1;j<=d;j++) Cnts[j]+=cnts[j],NUM[j]+=num[j],num[j]=cnts[j]=0;
}
Cnts[0]=1;int tt=0,D=1;
for (int p=x,i=fa[x];i>=1 && !vis[i];p=i,i=fa[i],D++) {
maxD=0;
for (int d,j=head[i];j;j=e[j].next) if (e[j].to!=fa[i] && !vis[e[j].to] && e[j].to!=p) {
deep[e[j].to]=1;caldeep(e[j].to,i);
for (d=0;cnts[d+1];d++);
maxD=max(maxD,d);
}
for (int k=1;k<=maxD;k++)
for (int l=k;l<=maxD;l+=k) num[k]+=cnts[l];
tt=max(tt,maxD);
for (int j=1;j<=min(block,maxD);j++) {
if (F[j][D%j]==-1) {
F[j][D%j]=0;
for (int k=j-(D-1)%j-1;k<=maxd;k+=j) F[j][D%j]+=Cnts[k];
}
ans[j]+=F[j][D%j]*num[j];
}
for (int j=block+1;j<=maxD;j++) {
LL tmp=0;
for (int k=j-(D-1)%j-1;k<=maxd;k+=j) tmp+=Cnts[k];
ans[j]+=tmp*num[j];
}
for (int i=1;i<=maxD;i++) num[i]=cnts[i]=0;
}
D--;int l=0,r=-1;LL tmp=0;
for (int i=1;i<=D+maxd;i++) {
tmp+=r+1<i ? Cnts[++r] : 0;
tmp-=l<i-D ? Cnts[l++] : 0;
ANS[i]+=tmp;
}
for (int i=1;i<=min(block,tt);i++)
for (int j=0;j<=D;j++) F[i][j]=-1;
for (int i=0;i<=maxd;i++) NUM[i]=Cnts[i]=0;
for (int i=head[x];i;i=e[i].next) if (!vis[e[i].to]) {
sum=size[e[i].to];Dargen=0;
caldargen(e[i].to,0);
work(Dargen);
}
} int main() {
scanf("%d",&n);
block=(int)sqrt(n)+0.5;
for (int i=2;i<=n;i++) {
scanf("%d",&fa[i]);
link(fa[i],i);
}
memset(F,-1,sizeof(F));
f[Dargen=0]=inf;sum=n;
caldargen(1,0);
work(Dargen);
for (int i=n-1;i>=1;i--)
for (int j=i+i;j<=n-1;j+=i) ans[i]-=ans[j];
for (int i=1;i<n;i++) printf("%lld\n",ans[i]+ANS[i]);
return 0;
}

  

【uoj33】 UR #2—树上GCD的更多相关文章

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

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

  2. [UOJ UR #2]树上GCD

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

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

    #33. [UR #2]树上GCD 有一棵$n$个结点的有根树$T$.结点编号为$1…n$,其中根结点为$1$. 树上每条边的长度为$1$.我们用$d(x,y)$表示结点$x,y$在树上的距离,$LC ...

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

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

  5. uoj33 【UR #2】树上GCD

    题目 大致是长剖+\(\rm dsu\ on\ tree\)的思想 先做一个转化,改为对于\(i\in[1,n-1]\)求出有多少个\(f(u,v)\)满足\(i|f(u,v)\),这样我们最后再做一 ...

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

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

  7. 【UR #2】树上GCD

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

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

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

  9. uoj33 树上GCD

    题意:给你一棵树,根节点为1,每条边长度为1.定义f(u,v)=gcd(u-lca(u,v),lca(u,v)-v),求有多少个无序点对f(u,v)=i.对每个i输出答案. n<=20W. 标程 ...

随机推荐

  1. c#随机生成中文姓名

    为什么要自己写这个生成器呢?大家应该都有过为测试数据发愁的时候,我就是出于这样的原因. 尽管本次代码很少,但是还会有后续的生成器分享出来. 我代码底子还不是很好,希望各位同道能够发表意见,同是也欢迎大 ...

  2. 小球下落(Dropping Balls, Uva 679)

    题目描述 有一棵二叉树,最大深度为D,且所有的叶子深度都相同.所有结点从上到下从左到右编号为1,2,3,-,2eD-1.在结点1处放一个小球,它会往下落.每个结点上都有一个开关,初始全部关闭,当每次有 ...

  3. iOS开发日常遇到问题记录

    1. [self.navigationController.navigationBar setTranslucent:NO]; iOS 7 之后,setTranslucent=yes 默认的   则状 ...

  4. 机器装多个版本php,并安装redis插件报错【已解决】

    机器原版本php5.5.3 适应新的框架安装了7.1.12 期间遇到的小问题就是安装 redis插件的时候,总报错,报错如下: Starting php-fpm [02-Jan-2019 10:15: ...

  5. 第二次作业(homework-02)成绩公布

    学位后三位和对应成绩: 057 0008 4011 4012 7014 5015 5017 6018 0019 0026 2027 7036 0038 7.5046 7048 6.5051 0061 ...

  6. 实验1:java开发环境的熟悉

    一.实验内容 1. 使用JDK编译.运行简单的Java程序 2.使用Eclipse 编辑.编译.运行.调试Java程序 3.实现四则运算并进行测试. 二.实验知识点 1. JVM.JRE.JDK的安装 ...

  7. 一个简单的加减乘除自动生成小程序(JAVA)

    在学习软件工程的时候,遇到一个这样的问题,一个程序员的儿子上小学二年级,老师让家长每天出30道加减题目给学生做,由于家长是个程序员,所以呢,他就自己写了个程序实现,我们可爱的老师于是也叫我们写了一个类 ...

  8. SpringMVC入门学习案例笔记

    一.数据库环境用mysql,数据库建表语句如下: /* SQLyog v10.2 MySQL - 5.1.72-community : Database - mybatis ************* ...

  9. C++获取private的变量-偷走private

    private提供了对数据的封装,使得private成员只能被类自身的成员函数以及类的友元访问,其他的函数或者类想要访问private成员只能通过该类所提供的set和get的方法进行访问, 或者返回其 ...

  10. python基础(五)函数

    一.函数概念 函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的,编程中的函数在英文中也有很多不同的叫法.在BASIC中叫做subroutine(子过程或子程序),在Pascal ...