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. 零基础学python之函数与模块(附详细的代码和安装发布文件过程)

    代码重用——函数与模块 摘要:构建函数,创建模块,安装发布文件,安装pytest和PEP 8插件,确认PEP8兼容性以及纠错 重用代码是构建一个可维护系统的关键. 代码组是Python中对块的叫法. ...

  2. 通过iLO进行Zabbix监控——针对HP服务器集成

    iLO 全名是 Integrated Lights-out,它是惠普某些型号的服务器上集成的远程管理端口,它能够允许用户基于不同的操作系统从远端管理服务器,实现了虚拟存在和控制,从而进行智能型基础构架 ...

  3. 基于Neutron的Kubernetes SDN实践经验之谈

    首先,向大家科普下Kubernetes所选择的CNI网络接口,简单介绍下网络实现的背景. CNI即Container Network Interface,是一套容器网络的定义规范,包括方法规范.参数规 ...

  4. virtual box下安装ubuntu经验

    1. 哪怕下载的是ubuntu64位版本,也在vitualbox下选择ubuntu而不要选择ubuntu(64bit) 2. 安装VBoxGuestAdditional.iso:下载和vbox版本相匹 ...

  5. cd命令详解

    基础命令学习目录首页 cd 进入用户主目录: cd ~ 进入用户主目录: cd - 返回进入此目录之前所在的目录: cd .. 返回上级目录(若当前目录为“/“,则执行完后还在“/":&qu ...

  6. mpstat命令详解

    基础命令学习目录首页 原文链接:https://www.cnblogs.com/ggjucheng/archive/2013/01/13/2858775.html 简介 mpstat是Multipro ...

  7. C++多态深入分析!

    以下分析是基于VS2010的.以后会使用G++分析看看G++如何处理多态! // polymorphic_test.cpp : 定义控制台应用程序的入口点. // /** 特别注意:实现C++多态,除 ...

  8. Python之并发编程-concurrent

    方法介绍 #1 介绍 concurrent.futures模块提供了高度封装的异步调用接口 ThreadPoolExecutor:线程池,提供异步调用 ProcessPoolExecutor: 进程池 ...

  9. 利用原生Javascript实现计算器(未完待续)

    这里,将记录我升级四则运算v1.2的整个过程. 环境检测,杨说检测环境也是可以高兴到手舞足蹈的一件事. 为了实现自动化,Testing,查阅相关资料,我这里使用了node(这里为了npm).yoema ...

  10. 【Alpha】阶段第八次Scrum Meeting

    [Alpha]阶段第八次Scrum Meeting 工作情况 团队成员 今日已完成任务 明日待完成任务 刘峻辰 编写按学院搜索课程接口 编写获得所有学院接口 赵智源 构建前测试点测试框架 编写alph ...