题意:给你一棵树,根节点为1,每条边长度为1。定义f(u,v)=gcd(u-lca(u,v),lca(u,v)-v),求有多少个无序点对f(u,v)=i。对每个i输出答案。 n<=20W.

标程:

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<vector>
  4. using namespace std;
  5. typedef long long ll;
  6. const int N=;
  7. int n,fa[N],f[N],g[N],sum[N],jp[N],dep[N],s1,s2;
  8. vector<int> vec[N];
  9. ll ans[N];
  10. const int th=;
  11. int main()
  12. {
  13. scanf("%d",&n);
  14. for (int i=;i<=n;i++) scanf("%d",&fa[i]),dep[i]=dep[fa[i]]+,sum[dep[i]]++;
  15. for (int i=n;i>=;i--) sum[i]+=sum[i+],jp[i]=i;//u-v为链的答案
  16. for (int d=;d<=min(th,n);d++)
  17. {
  18. for (int i=;i<=n;i++) f[i]=g[i]=;
  19. for (int i=n;i>=;i--)
  20. {
  21. g[jp[i]]+=f[i]+; jp[i]=fa[jp[i]];
  22. ans[d]+=(ll)f[fa[i]]*g[i]; f[fa[i]]+=g[i];
  23. }
  24. }
  25. for (int i=n;i>;i--)
  26. {
  27. vec[i].push_back();//距离i有0个单位长的点有一个(i自己),相对fa[i]实现平移
  28. if ((s1=vec[i].size())>(s2=vec[fa[i]].size()))//启发式合并
  29. swap(vec[i],vec[fa[i]]),swap(s1,s2);
  30. if (s1>th&&s2>th)
  31. {
  32. for (int d=th+;d<=s1;d++)
  33. {
  34. int g1=,g2=;
  35. for (int j=d;j<=s1;j+=d) g1+=vec[i][s1-j];
  36. for (int j=d;j<=s2;j+=d) g2+=vec[fa[i]][s2-j];
  37. ans[d]+=(ll)g1*g2;
  38. }
  39. }
  40. for (int j=;j<=s1;j++) vec[fa[i]][s2-j]+=vec[i][s1-j];
  41. vec[i].clear();//清掉多余空间
  42. }
  43. for (int i=n;i>=;i--)//容斥
  44. for (int j=*i;j<=n;j+=i) ans[i]-=ans[j];
  45. for (int i=;i<n;i++) printf("%lld\n",ans[i]+sum[i]);
  46. return ;
  47. }

题解:容斥+阈值+树形dp+启发式合并:

暴力1:枚举u,v,logn求出lca。算出深度,统计答案。O(n^2logn)

暴力2:枚举根节点u,统计子树中深度为d的链的数量。将cnt[i]*cnt[j]统计入ans[gcd(i,j)],注意不要统计同一棵子树的答案。因为树随机,所以树高log,O(nlog^2(n))。

暴力3:对于第4个测试点,发现是若干条链拎起来。我们考虑容斥,ans[d]表示gcd是d的倍数的点对的个数。

如果u-v在同一条链上,那么f(u,v)=dis(u,v).在最后加上即可。反之,对于每一条链统计出长度是d的倍数的数量[h/d],求在任意两条树链上各取一条d倍数的链的方案数。用(x1^2+x2^2+...+xn^2)=x1^2+x2^2+...+xn^2+2*sigma[1<=i<j<=n]xi*xj的恒等式可以很快算出。复杂度O(n)。

正解1:继续用容斥。树形dp求出f[i]表示i的子树中d的倍数的链有几条,辅助数组g[i]表示距i的距离%d=d-1的点数。pd[i]表示i的d-1阶祖先。

f[i]<-g[son],ans[d]<-f[i]*g[son],g[pd[i]]<-f[i]+1。取阈值th,时间复杂度O(n*th)。

再一。用启发式合并从底至上维护vec[size-i][d]表示i的子树中深度恰好为d的点有几个(倒序对子节点的合并前的平移比较方便)。

合并vec[x]和vec[fa[x]]的时候就对于每个d>th,统计下深度为d的倍数的点的个数。乘法原理一下。每个点会被合并logn次,合并复杂度nlogn。计算一对父子的时间有nlogn,而深度>th的点不超过[n/th]个。时间复杂度为n^2logn/th。为了避免空间vector有n^2,每次用儿子更新完父亲后对儿子vector清零,空间复杂度O(n)。

取th=根号n*logn最优。

正解2:点分治,按深度点分(当然最后还是用容斥)。取出当前树重心c。

考虑所有 u→lca(u,v)→v的路径经过c产生的贡献。要么两个点都在c的子树内即lca=c,要么一个点在c的子树内。

1.两个点都在c子树内,直接用正解1的dp。深度为logn,总点数为n/2+n/4+n/8+...+1<n,复杂度为O(nlogn)。

2.u 在 c 的子树内,v 不在。对于从c到当前子树root路径上的所有点ai,都有可能作为u和v的lca。对于ai旁边的子树和c的子树统计(u,v)的答案。

枚举d,用dep/d的时间统计ai旁边子树中深度是d的倍数的点数cnt和。

我们还需要知道 c 的子树中有多少点相对于 ai 的高度是 d 的倍数,也就是说我们要在c子树的 cnt 数组中查询下标间隔为 d 的子序列中的元素之和。

注意到间隔为 d 时,至多只有 d 种这样的子序列,对重复查询记忆化。

当d<根号dep时,复杂度<d*dep/d=dep,总共是O(dep^1.5);当d>根号dep时,单次对d查询复杂度为 dep/d<根号dep。

所以一层分治复杂度为O(n^1.5).根据主定理,总复杂度为O(n^1.5)。

uoj33 树上GCD的更多相关文章

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

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

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

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

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

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

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

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

  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 UR #2]树上GCD

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

  9. UOJ#33. 【UR #2】树上GCD 点分治 莫比乌斯反演

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ33.html 题解 首先我们把问题转化成处理一个数组 ans ,其中 ans[i] 表示 d(u,a) 和 ...

随机推荐

  1. jsonp的原理,应用场景,优缺点

    在开发测试中,难免会在不同域下进行跨域操作,出于安全性考虑,浏览器中的同源策略阻止从一个域上加载的脚本获取或者操作 另一个域下的文档属性,这时需要进行跨域的方式进行解决,如:使用jsonp ,ifra ...

  2. Spring-Security (学习记录五)--配置登录时,密码采用md5加密,以及获取登录信息属性监听同步自己想要的登录信息

    目录 1. PasswordEncoder 采用密码加密 2. 获取当前的用户信息 1. PasswordEncoder 采用密码加密 使用前面的例子.可以看出我们数据库密码是采用明文的,我们在登录的 ...

  3. boost库之pool编译错误

    1,编译错误截图 2,解决方法 这是由于没有链接对应的库导致的错误,在编译命令中加上 -lboost_system选项即可.

  4. Python3数据分析与挖掘建模实战✍✍✍

    Python3数据分析与挖掘建模实战 Python数据分析简介 Python入门 运行:cmd下"python hello.py" 基本命令: 第三方库 安装 Windows中 p ...

  5. 码云的使用以及pycharm

    码云的使用 下载Git一路next 在码云上新建仓库 建立码云仓库 ​ 右键 git bash hero 依次输入 git config --global user.name "用户名&qu ...

  6. 鼠标悬浮到div上,div进行360°旋转

    <!DOCTYPE html> <html> <head> <title>旋转</title> </head> <styl ...

  7. ExecutorService线程池submit的使用

    有关线程池ExecutorService,只谈submit的使用 可创建的类型如下: private static ExecutorService pool = Executors.newFixedT ...

  8. 用多线程发送邮箱(一次给一个用户发送N封邮件)

    前台不用写,后台执行方法就可以了. namespace SendMail { public partial class SendMail_Page : System.Web.UI.Page { pro ...

  9. css---8 过渡属性刨析

    1.       transition-property 默认值为 all,表示所有可被动画的属性都表现出过渡动 可以指定多个 property 属性值: none 没有过渡动画. all 所有可被动 ...

  10. 配置类一@CrossOrigin

    @CrossOrigin是用来处理跨域请求的注解 跨域:指的是浏览器不能执行其他网站的脚本.它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制. 所谓同源是指,域名,协议,端口 ...