【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. 标程 ...
随机推荐
- gym100676 [小熊骑士限定]2015 ACM Arabella Collegiate Programming Contest
Kuma Rider久违的第二场训练,这场很水,又在vj的榜单上看到第一场的大哥了,2小时ak,大哥牛啤! A.水 #include<cstdio> #include<iostrea ...
- 4星|《财经》2018年第10期:远程视界自我定位为“专科远程医疗联合体O2O平台”,主要盈利模式就是做融资租赁
<财经>2018年第10期 总第527期 旬刊 本期主要内容:做远程医疗资金链断裂:人工智能时代有可能让刘易斯观点论失败:小米的盈利模式刨析:陆奇在百度的改革.其中1.4都成了朋友圈热文. ...
- centos 开机自启设定:
在sentos系统下,主要有两种方法设置自己安装的程序开机启动.1.把启动程序的命令添加到/etc/rc.d/rc.local文件中,比如下面的是设置开机启动httpd. #!/bin/sh # # ...
- linux运维升级路线
运维工程师是从一个呆逼进化为苦逼再成长为牛逼的过程,前提在于你要能忍能干能拼,还要具有敏锐的嗅觉感知前方潮流变化.如:今年大数据,人工智能比较火……(相对表示就是 Python 比较火) 之前写过运维 ...
- python-gevent模块实现socket大并发
服务器端:gevent_server.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ...
- 安装keystone时创建用户失败
系统:centos7.3 版本:openstack ocata 1.问题描述 安装keystone在创建用户时报错: The request you have made requires authen ...
- Streamr助你掌控自己的数据(2)——三种整合数据至Streamr的典型场景
博客说明 所有刊发内容均可转载但是需要注明出处. 三种整合数据至Streamr的典型场景 本系列文档主要介绍怎么通过Streamr管理自己的DATA,整个系列包括三篇教程文档,分别是:教你5分钟上传数 ...
- Redux和React-Redux的实现(二):Provider组件和connect的实现
接着上一篇讲,上一篇我们实现了自己的Redux和介绍了React的context以及Provider的原理. 1. Provider组件的实现 Provider组件主要有以下下两个作用 在整个应用上包 ...
- C# Js 时间格式化问题
C# 后台: .ToString("dd-MMM-yyyy", System.Globalization. DateTimeFormatInfo.InvariantInfo) eg ...
- Daily Scrum (2015/11/6)
今晚除了玉钟焕的其他成员在一起开了个短会.讨论有关添加新功能以及一些BUG问题.由于时间原因,我们本想把动态爬取功能留到第二个迭代中,但是现在目前时间还够,我们便一起对这一功能的讨论和实现进行分析. ...