【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. 标程 ...
随机推荐
- 基本数据结构 -- 栈简介(C语言实现)
栈是一种后进先出的线性表,是最基本的一种数据结构,在许多地方都有应用. 一.什么是栈 栈是限制插入和删除只能在一个位置上进行的线性表.其中,允许插入和删除的一端位于表的末端,叫做栈顶(top),不允许 ...
- Tomcat部署与使用
Tomcat简介 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun和其他一些公司及个人共同开发 ...
- Vue 入门之组件化开发
Vue 入门之组件化开发 组件其实就是一个拥有样式.动画.js 逻辑.HTML 结构的综合块.前端组件化确实让大的前端团队更高效的开发前端项目.而作为前端比较流行的框架之一,Vue 的组件和也做的非常 ...
- No.111_第四次团队会议
后端的偏执 啊,这次又轮到我写团队博客了. 此时又是深夜,窗外漫天繁星.舍友的呼噜声惊吓了月亮,它害羞地跑回了云里去. 我关上灯拔掉机械,悄悄拿着电脑上了床,写这次的团队博客.曾经觉得自己绝对不会晚睡 ...
- 20172319 2018.04.11-16 《Java程序设计教程》 第6周学习总结
20172319 2018.04.11-16 <Java程序设计教程>第6周学习总结 目录 教材学习内容总结 教材学习中的问题和解决过程 代码调试中的问题和解决过程 代码托管 上周考试错题 ...
- 冲刺One之站立会议2
在确定了总体目标之后,我们先决定了实现的具体功能,包括一个登陆界面,一个聊天室的主界面和服务器端的内容.我们今天完成了一小部分内容,把每个内容的主体框架搭建了起来. 效果如下图所示: 燃尽图2
- 《Spring1之第八次站立会议》
<第八次站立会议> 昨天:我查找了关于实现视频功能的相关代码. 今天:对用C#写的视频功能进行了相关的了解. 遇到的问题:由于对C#不是很了解,所以其中的有些代码还是看不懂.
- 关于地图首页会卡 button background惹的祸
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sat=&q ...
- Teamwork(The fifth day of the team)
在前面几天的努力中,我们已经完成了一些自己的工作,还有的就是一些完善,因为在前段时间一直都在寻找和配置Eclipse+Android SDK,由于版本和一些网络的阻碍,总是不能如愿的很好完成,经过了一 ...
- 二路归并排序java实现
二路归并排序:其核心思想时将问题一分为二,并递归调用一分为二方法,使问题分割到不能再分各的原子问题,然后再归并,从实现原子问题开始,层层向上归并,最终解决整体问题.即所谓“分而治之,万流归一” 二路归 ...