【uoj33】 UR #2—树上GCD
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的更多相关文章
- UOJ33 [UR #2] 树上GCD 【点分治】【容斥原理】【分块】
题目分析: 树上点对问题首先想到点分治.假设我们进行了点分治并递归地解决了子问题.现在我们合并问题. 我们需要找到所有经过当前重心$ c $的子树路径.第一种情况是LCA为当前重心$ c $.考虑以$ ...
- [UOJ UR #2]树上GCD
来自FallDream的博客,未经允许,请勿转载,谢谢. 传送门 看完题目,一般人都能想到 容斥稳了 .这样我们只要统计有多少点对满足gcd是i的倍数. 考虑长链剖分,每次合并的时候,假设我已经求出轻 ...
- 【UOJ#33】【UR#2】树上GCD 有根树点分治 + 容斥原理 + 分块
#33. [UR #2]树上GCD 有一棵$n$个结点的有根树$T$.结点编号为$1…n$,其中根结点为$1$. 树上每条边的长度为$1$.我们用$d(x,y)$表示结点$x,y$在树上的距离,$LC ...
- 【UOJ#33】【UR #2】树上GCD(长链剖分,分块)
[UOJ#33][UR #2]树上GCD(长链剖分,分块) 题面 UOJ 题解 首先不求恰好,改为求\(i\)的倍数的个数,最后容斥一下就可以解决了. 那么我们考虑枚举一个\(LCA\)位置,在其两棵 ...
- uoj33 【UR #2】树上GCD
题目 大致是长剖+\(\rm dsu\ on\ tree\)的思想 先做一个转化,改为对于\(i\in[1,n-1]\)求出有多少个\(f(u,v)\)满足\(i|f(u,v)\),这样我们最后再做一 ...
- [UOJ]#33. 【UR #2】树上GCD
题目大意:给定一棵有根树,边长均为1,对于每一个i,求树上有多少个点对,他们到lca距离的gcd是i.(n<=200,000) 做法:先容斥,求出gcd是i的倍数的点对,考虑长链剖分后从小到大合 ...
- 【UR #2】树上GCD
这道题是有根树点分治+烧脑的容斥+神奇的分块 因为是规定1为根,还要求LCA,所以我们不能像在无根树上那样随便浪了,必须规定父亲,并作特殊讨论 因为gcd并不好求,所以我们用容斥转化一下,求x为gcd ...
- UOJ#33. 【UR #2】树上GCD 点分治 莫比乌斯反演
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ33.html 题解 首先我们把问题转化成处理一个数组 ans ,其中 ans[i] 表示 d(u,a) 和 ...
- uoj33 树上GCD
题意:给你一棵树,根节点为1,每条边长度为1.定义f(u,v)=gcd(u-lca(u,v),lca(u,v)-v),求有多少个无序点对f(u,v)=i.对每个i输出答案. n<=20W. 标程 ...
随机推荐
- spring 在ssh三大框架中充当的角色
https://blog.csdn.net/yeah_nn/article/details/79992777
- ssh软件及命令的使用
常用软件安装及使用目录 第1章 ssh常用用法小结 1.1 连接到远程主机: 命令格式 : ssh name@remoteserver 或者 ssh remoteserver -l name 说明:以 ...
- 10款常见MySQL高可用方案选型解读
一.概述 我们在考虑MySQL数据库的高可用架构时,主要考虑如下几方面: 如果数据库发生了宕机或者意外中断等故障,能尽快恢复数据库的可用性,尽可能的减少停机时间,保证业务不会因为数据库的故障而中断. ...
- PHP中你应该知道的require()文件包含的正确用法
以前看一些PHP框架源码的时候,很奇怪在文件包含的时候,会用dirname(__FILE__)来拼凑文件路径,不知道这样做有什么好处,后来终于发现了其中的缘由. 我们来看一个简单的例子: 有a,b,c ...
- 兼容所有浏览器的旋转效果-IE滤镜Matrix和CSS3transform
在现代浏览器中使用CSS3的transform样式即可轻松搞定,但是对于国内IE浏览器(特别是7,8)还占有较大份额的情况下,兼容性还是必须要考虑的,所以也特意记录下IE旋转滤镜的使用. 在IE下的旋 ...
- Bing词典vs有道词典比对测试报告——体验篇之成长性及用户控制权
成长性: 会记住曾经查询过的单词或例句与有道词典实现基本一样,并无特别亮点. 用户有控制权: 必应词典和有道词典都能实现基本的查询前进和后退.以及无法查找结果,能顺利进行反馈. 我们在输入完单词按下回 ...
- 关于echart柱形图的使用问题
关于一个数据对应两个值的问题 series: [{ name: '数量(个)', type: 'bar', barWidth: '30%', barGap: , //两个数据条没有间距 data: y ...
- Myeclipse(2014)项目的注释乱码
(之前都是在项目右键 propertits----resource---text file encoding 里面改成UTF-8的 下面是以后都直接换) window->preference-& ...
- JAVA 对象序列化——Serializable
1.序列化是干什么的? 简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来.虽然你可以用你自己的各种各样的方法来保存object st ...
- 微信小程序之Flex布局
微信小程序页面布局方式采用的是Flex布局.Flex布局,是W3c在2009年提出的一种新的方案,可以简便,完整,响应式的实现各种页面布局.Flex布局提供了元素在容器中的对齐,方向以及顺序,甚至他们 ...