题目

大致是长剖+\(\rm dsu\ on\ tree\)的思想

先做一个转化,改为对于\(i\in[1,n-1]\)求出有多少个\(f(u,v)\)满足\(i|f(u,v)\),这样我们最后再做一个反演就好了

既然我们要求有多少对\(f(u,v)\)是\(i\)或\(i\)的倍数,我们需要在长剖的时候快速合并两边的信息,这个信息长得非常别致,形如到当前节点距离为\(i\)或\(i\)的倍数的节点个数

轻儿子这边还好说,我们直接暴力调和级数处理一下即可,但是这样的信息从中儿子哪里却非常不好继承

考虑一下根号分治

如果这个\(i>\sqrt{n}\),也就是直接暴力从重儿子那里暴力复杂度是\(\frac{n}{i}<\sqrt{n}\),这样我们自然可以直接利用长剖时维护的数组得到

但是\(i\leq \sqrt{n}\)时,考虑维护一个数组可以快速得到这样的信息,但是这样的数组从重儿子那里没办法继承,看起来长剖好像行不太通

这个时候就需要用\(\rm dsu\)的思想了

考虑维护一个\(g[i][j]\)表示一个点子树内部点深度对\(i\)取模余数为\(j\)的点的个数,这样我们就没有必要考虑如何继承,像重儿子那样不清空直接拿来用就好了

利用这个数组我们很方便查询子树内部到当前节点距离为\(i\)或\(i\)的倍数的点的个数,只需要利用当前点的深度搞一搞就可以了

我们暴力轻儿子的时候也可以直接把更新\(g\),更新一次复杂度是\(O(\sqrt{n})\)的

但一般来说,长剖都是先重儿子再轻儿子,但是\(\rm dsu\)却是先轻儿子再重儿子,这里为了保证\(g\)里维护的信息都来自于子树内部,我们必须得像\(\rm dsu\)一样先轻儿子之后处理重儿子

代码

  1. #include<bits/stdc++.h>
  2. #define re register
  3. #define LL long long
  4. #define min(a,b) ((a)<(b)?(a):(b))
  5. inline int read() {
  6. char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
  7. while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
  8. }
  9. const int maxn=2e5+5;
  10. struct E{int v,nxt;}e[maxn];
  11. int n,num,__,B;
  12. int head[maxn],son[maxn],len[maxn],dep[maxn],f[maxn],mu[maxn],p[maxn>>1];
  13. int h[maxn],top[maxn],dfn[maxn],g[320][320],b[maxn],c[maxn];
  14. LL ans[maxn],Ans[maxn];
  15. inline void add(int x,int y) {
  16. e[++num].v=y;e[num].nxt=head[x];head[x]=num;
  17. }
  18. void dfs1(int x) {
  19. for(re int i=head[x];i;i=e[i].nxt) {
  20. dep[e[i].v]=dep[x]+1;dfs1(e[i].v);
  21. if(len[e[i].v]>len[son[x]]) son[x]=e[i].v;
  22. }
  23. len[x]=len[son[x]]+1;
  24. }
  25. void dfs2(int x,int topf) {
  26. top[x]=topf,dfn[x]=++__;
  27. if(!son[x]) return;
  28. dfs2(son[x],topf);
  29. for(re int i=head[x];i;i=e[i].nxt)
  30. if(son[x]!=e[i].v) dfs2(e[i].v,e[i].v);
  31. }
  32. inline void ins(int x,int v) {
  33. for(re int i=1;i<=B;++i) g[i][x%i]+=v;
  34. }
  35. inline int calc(int x,int k) {
  36. if(k<=B) return g[k][dep[x]%k];
  37. int tot=0;
  38. for(re int i=dfn[x]+k;i<=dfn[x]+len[x]-1;i+=k) tot+=h[i];
  39. return tot;
  40. }
  41. void dfs(int x) {
  42. for(re int i=head[x];i;i=e[i].nxt)
  43. if(son[x]!=e[i].v) dfs(e[i].v);
  44. if(son[x]) dfs(son[x]);
  45. for(re int i=head[x];i;i=e[i].nxt) {
  46. if(son[x]==e[i].v) continue;
  47. int y=e[i].v;
  48. for(re int j=1;j<=len[y];++j)
  49. b[j]=h[dfn[y]+j-1];
  50. for(re int j=1;j<=len[y];++j)
  51. for(re int k=j;k<=len[y];k+=j) c[j]+=b[k];
  52. for(re int j=1;j<=len[y];++j)
  53. ans[j]+=1ll*c[j]*calc(x,j);
  54. for(re int j=1;j<=len[y];++j) c[j]=b[j]=0;
  55. for(re int j=1;j<=len[y];++j)
  56. ins(j+dep[y]-1,h[dfn[y]+j-1]),h[dfn[x]+j]+=h[dfn[y]+j-1];
  57. }
  58. ins(dep[x],1);h[dfn[x]]++;
  59. if(x==top[x]) {
  60. for(re int i=1;i<=B;++i)
  61. for(re int j=dep[x];j<=dep[x]+len[x]-1;++j)
  62. g[i][j%i]=0;
  63. }
  64. }
  65. int main() {
  66. n=read();
  67. for(re int x,i=2;i<=n;++i) x=read(),add(x,i);
  68. dep[1]=1,dfs1(1),dfs2(1,1);f[1]=mu[1]=1;
  69. B=std::ceil(std::sqrt(n));B=min(B,310);dfs(1);
  70. for(re int i=2;i<=n;i++) {
  71. if(!f[i]) p[++p[0]]=i,mu[i]=-1;
  72. for(re int j=1;j<=p[0]&&p[j]*i<=n;++j) {
  73. f[p[j]*i]=1;if(i%p[j]==0) break;
  74. mu[p[j]*i]=-mu[i];
  75. }
  76. }
  77. for(re int i=1;i<=n;i++)
  78. for(re int j=i;j<=n;j+=i)
  79. Ans[i]+=1ll*mu[j/i]*ans[j];
  80. for(re int i=2;i<=n;++i)
  81. c[1]++,c[dep[i]]--;
  82. for(re int i=1;i<=n;i++) c[i]+=c[i-1];
  83. for(re int i=1;i<n;i++) printf("%lld\n",Ans[i]+c[i]);
  84. return 0;
  85. }

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

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

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

  2. 【uoj33】 UR #2—树上GCD

    http://uoj.ac/problem/33 (题目链接) 题意 给出一棵${n}$个节点的有根树,${f_{u,v}=gcd(dis(u,lca(u,v)),dis(v,lca(u,v)))}$ ...

  3. [UOJ UR #2]树上GCD

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

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

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

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

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

  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. 制作linux根文件系统

    转载地址:http://wenku.baidu.com/view/2cb1b4707fd5360cba1adb14.html 2011十一月 24 转 嵌入式根文件系统制作 (3985) (0) 转自 ...

  2. SSM框架整合思路

    SSM框架整合思路 Spring在整合中起到的作用(面试时常问到) Spring管理持久层的mapper. Spring管理业务层的service,service可以调用mapper接口.Spring ...

  3. java.util.Arrays,java.lang.Math,java.lang.System 类的常用方法汇总

    java.util.Arrays类是数组的工具类,一般数组常用的方法包括 二分查找:public static int  binarySearch(array[],int key),返回key的下标i ...

  4. 专题:OpenSSH tunneling

    SSH tunneling 相关 参考資料:http://www.ibm.com/developerworks/cn/linux/l-cn-sshforward/ 本地端口转发: 适用场景:发起端可以 ...

  5. springboot+mybatis+layUI

    1.idea快速搭建 2.生成后目录结构 3.引入layui-2.4.5 4.static/新建index.html,页面代码参考https://www.layui.com/doc/element/l ...

  6. CM集群管理

    用CM装完cdh 版本后,将不同机架的服务器已经认为拆掉网线,但是主机管理那边还是显示已经下载的服务器存在. 如图: 该怎么配置 node8 和node9会自动消失:     DataNode已经显示 ...

  7. DNF抽奖活动

    活动内容: DNF用户在注册页面注册获得抽奖资格(或分享好友注册)参与抽奖,产生奖品后,活动参与用户,在活动领奖页面领取奖品,金币及点券需填写相应游戏区服.qq号等信息,并且为防止活动刷子,在领取页提 ...

  8. 6-MySQL-Ubuntu-操作数据表的基本操作(一)

    注: SQL语句的关键字不区分大小写,如select 和Select都可以 (1) 查看当前使用的数据库; select database(); (2) 使用某数据库或切换到某数据库 use 数据库名 ...

  9. php的生命周期的概述

    1. PHP是随着WEB服务器(apache)的启动而运行的: 2. PHP通过mod_php5.so()模块和服务器(apache)相连 3. PHP总共有三个模块:内核.Zend引擎.以及扩展层: ...

  10. ubuntu16.04安装python虚拟环境

    自己也是搜的教程,亲测有效 ubuntu16.04创建虚拟环境 一.linux环境 Ubuntu16.04 二.安装和配置虚拟环境 安装虚拟环境 sudo pip install virtualenv ...