题目分析:

树上点对问题首先想到点分治。假设我们进行了点分治并递归地解决了子问题。现在我们合并问题。

我们需要找到所有经过当前重心$ c $的子树路径。第一种情况是LCA为当前重心$ c $。考虑以$ 1 $为根的$ c $的子树。那么首先在子问题中先斥掉不经过$ c $的路径。然后对于$ c $的子树处理出距离数组。用桶存储。

从大到小枚举最大公因数$ d $,求出所有距离为$ d $倍数的点的个数。然后做乘法得到$ num1 $。再考虑$ num1 $中GCD不等于$ d $的数有哪些。实际上我们早就计算出了GCD为$ d $的倍数的情况了,只需要把这一部分斥掉就行了。所以处理$ c $的子树的点对的时间复杂度是$ O(nlogn) $。

再考虑$ c $的祖先的儿子到$ c $的子树中的点的情况。不难想到类似的处理方法。开个桶存储距离。由于点分治的特性。我们很容易就可以证明这样所有桶的最大值之和是不会超过$ O(n) $的。这样枚举的时间复杂度是有保障的。对于$ c $的某个祖先的子树中的点,枚举GCD为$ d $。 那么我们要在$ c $的子树中找到所有的距$ c $祖先$ d $的倍数的点。 由于我们拔高了LCA,这时候我们不能简单地枚举倍数。重新审视问题,发现其实它是求的c中从i开始每隔j个的和。采用分块算法,对于小于等于$ \sqrt{n} $的情况共有$ \sqrt{n} $个不同的起点。每个不同的间隔都会完全覆盖依次整个桶。共覆盖了$ \sqrt{n} $次。所以预处理的时间复杂度为$ O(n*\sqrt{n}) $。对于大于$ \sqrt{n} $的情况可以暴力计算。

我们每处理一个重心的时间复杂度为$ O(n*\log n+n*\sqrt{n}) $分为两半,前半部分总时间复杂度为$ O(n*\log n*\log n) $,后半部分带入主定理知为$ O(n*\sqrt{n}) $

代码:

 #include<bits/stdc++.h>
using namespace std; const int maxn = ; int n;
int dep[maxn],f[maxn];
long long ans[maxn];
vector <int> g[maxn]; int arr[maxn],sz[maxn],dg[maxn],presum[maxn]; void read(){
scanf("%d",&n);
for(int i=;i<=n;i++) {
scanf("%d",&f[i]);
g[f[i]].push_back(i);
g[i].push_back(f[i]);
}
} void DFS(int now,int dp) {
dep[now] = dp;
for(int i=;i<g[now].size();i++){
if(g[now][i] == f[now]) continue;
DFS(g[now][i],dp+);
}
} void dfs1(int now,int fa){
sz[now] = ;dg[now] = ;
for(int i=;i<g[now].size();i++){
if(g[now][i] == fa) continue;
if(arr[g[now][i]]) continue;
dfs1(g[now][i],now);
sz[now] += sz[g[now][i]];
dg[now] = max(dg[now],sz[g[now][i]]);
}
sz[now]++;
} int dfs2(int now,int fa,int tot){
int res = now;dg[now] = max(tot-sz[now],dg[now]);
for(int i=;i<g[now].size();i++){
if(g[now][i] == fa) continue;
if(arr[g[now][i]]) continue;
int z = dfs2(g[now][i],now,tot);
if(dg[res] > dg[z])res = z;
}
return res;
} int depest,h[maxn],sum[maxn];
long long nowans[maxn];
int Final[][]; int oth,arv[maxn],s2[maxn]; void dfs3(int now,int len,int stop){
h[len]++;depest = max(depest,len);
for(int i=;i<g[now].size();i++){
if(g[now][i] == f[now]) continue;
if(arr[g[now][i]]!= && arr[g[now][i]]<=stop) continue;
dfs3(g[now][i],len+,stop);
}
} void dfs4(int now,int cant,int stop,int lca){
arv[dep[now]-dep[lca]]++; oth = max(oth,dep[now]-dep[lca]);
for(int i=;i<g[now].size();i++){
if(g[now][i] == f[now] || g[now][i] == cant) continue;
if(arr[g[now][i]]!= && arr[g[now][i]]<=stop) continue;
dfs4(g[now][i],cant,stop,lca);
}
} void solve_dec(int now,int last,int ht){
depest = ;
dfs3(now,,ht);
for(int i=depest;i>=;i--){
for(int j=;i*j<=depest;j++) sum[i] += h[i*j];
nowans[i] = 1ll*sum[i]*(sum[i]-)/;
for(int j=;i*j<=depest;j++) nowans[i]-=nowans[i*j];
ans[i] -= nowans[i];
}
for(int i=;i<=depest;i++) h[i] = ,sum[i] = ,nowans[i] = ;
} void solve_add(int now,int ht){
depest = ;
dfs3(now,,ht);
for(int i=depest;i>=;i--){
for(int j=;i*j<=depest;j++) sum[i] += h[i*j]; //multi d
nowans[i] = 1ll*sum[i]*(sum[i]-)/;
for(int j=;i*j<=depest;j++) nowans[i]-=nowans[i*j];
ans[i] += nowans[i];
}
for(int i=;i<=depest;i++) nowans[i] = ; for(int i=;i*i<=depest;i++) for(int j=;j<i;j++)
for(int k=j;k<=depest;k+=i) Final[i][j] += h[k];
// in subtree int tp = f[now],last = now,rem = ;
while(tp&&(arr[tp]>ht||arr[tp] == )){
oth = ;rem++;
dfs4(tp,last,ht,tp); // tp mean lca
for(int i=oth;i>=;i--){
for(int j=;i*j<=oth;j++) s2[i] += arv[i*j];
if(i*i<=depest)
nowans[i] = 1ll*s2[i]*Final[i][(((-rem)%i)+i)%i];
else{
int frst = (((-rem)%i)+i)%i;
int tot = ;
for(int k =frst;k<=depest;k+=i) tot += h[k];
nowans[i] = 1ll*s2[i]*tot;
}
for(int j=;i*j<=oth;j++) nowans[i] -= nowans[i*j];
ans[i] += nowans[i];
}
last = tp; tp = f[tp];
for(int i=;i<=oth;i++) arv[i] = s2[i] = nowans[i] = ;
}
//out subtree
for(int i=;i<=depest;i++) h[i] = ,sum[i] = ;
for(int i=;i*i<=depest;i++) for(int j=;j<i;j++) Final[i][j] = ;
} void divide(int now,int last,int ht){
dfs1(now,); int pw = sz[now];
int pt = dfs2(now,,pw);
arr[pt] = ht;
for(int i=;i<g[pt].size();i++){
if(arr[g[pt][i]]) continue;
divide(g[pt][i],pt,ht+);
}
if(last&&f[now]==last) solve_dec(now,last,ht-);
solve_add(pt,ht);
} void work(){
DFS(,);
divide(,,);
for(int i=;i<=n;i++) presum[dep[i]-]++;
for(int i=n;i>=;i--) presum[i] += presum[i+];
for(int i=;i<=n;i++) ans[i] += presum[i];
for(int i=;i<n;i++) printf("%lld\n",ans[i]);
} int main(){
read();
work();
return ;
}

UOJ33 [UR #2] 树上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 点分治 莫比乌斯反演

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

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

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

  4. [UOJ UR #2]树上GCD

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

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

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

  6. poj1741 树上的点分治

    题意: 一棵10000个点的树,每条边的长不超过1000,给定一个值k,问距离不超过k的点对数有多少.(多组数据) 输入样例: 5 4 1 2 3 1 3 1 1 4 2 3 5 1 0 0输出样例: ...

  7. 【UOJ#50】【UR #3】链式反应(分治FFT,动态规划)

    [UOJ#50][UR #3]链式反应(分治FFT,动态规划) 题面 UOJ 题解 首先把题目意思捋一捋,大概就是有\(n\)个节点的一棵树,父亲的编号大于儿子. 满足一个点的儿子有\(2+c\)个, ...

  8. [BZOJ 2820] YY的gcd(莫比乌斯反演+数论分块)

    [BZOJ 2820] YY的gcd(莫比乌斯反演+数论分块) 题面 给定N, M,求\(1\leq x\leq N, 1\leq y\leq M\)且gcd(x, y)为质数的(x, y)有多少对. ...

  9. 还不会做! 树上的gcd 树分治 UOJ33

    题目链接:http://uoj.ac/problem/33 题解链接:http://vfleaking.blog.uoj.ac/blog/38 现在感觉到了做OI的层层递进的思路的伟大之处,作为一个大 ...

随机推荐

  1. 在项目中,多个方法会调用相同的sql语句,怎么解决各个方法的不同sql查询,解决冲突。

    公司的代码中sql语句,可能会被多个方法进行调用,但是有的方法会关联到别的表,这样的话,如果修改不当,那么同样调用该sql语句的方法,会出现报错. 最近做的公司的一个功能,就出现这样一个问题,虽然本功 ...

  2. BZOJ4237 JOISC2014 稻草人 CDQ分治、单调栈

    传送门 题意:给出平面上$N$个点,求满足以下两个条件的矩形:①左下角与右上角各有一个点:②矩形内部没有点.$N \leq 2 \times 10^5$,所有数字大于等于$0$,保证坐标两两不同 最开 ...

  3. 数据库日志——mysql与Oracle的日志

    一.MySQL 在数据同步中用的比较多的是MySQL的binlog 1.bin-log简介 它记录了所有的DDL和DML(除了数据查询语句,select与show不记录)语句,以事件形式记录,还包含语 ...

  4. [Oracle]为何Archivelog 没有马上被删除

    [Oracle]为何Archivelog 没有马上被删除 客户设置了 Archivelog 的 deletion policy 是 CONFIGURE ARCHIVELOG DELETION POLI ...

  5. Linux Shell完成Qt程序的自动部署

    #!/bin/sh #取当前脚本的绝对路径 srcDir=$(cd ")";pwd) #设置库所在路径 libDir=${srcDir}"/J1900RunLib/*&q ...

  6. 校内模拟赛 SovietPower Play With Amstar

    SovietPower Play With Amstar 题意: 一棵二叉树,每次询问一条路径上的路径和,初始每个点有一个权值1,询问后权值变为0.$n \leq 10^7,m\leq10^6$ 分析 ...

  7. ES6入门之let、cont

    一.前提 解决ES5中只有全局作用域和函数作用域,没有块级作用域而带来的不合理的场景. let 基本用法 用法和var 一样,只是let声明的变量只有在let命令所在的代码块有效 { let a = ...

  8. [HNOI2018]排列[堆]

    题意 给定一棵树,每个点有点权,第 \(i\) 个点被删除的代价为 \(w_{p[i]}\times i\) ,问最小代价是多少. 分析 与国王游戏一题类似. 容易发现权值最小的点在其父亲选择后就会立 ...

  9. React 等框架使用 index 做 key 的问题

    React 等框架使用 index 做 key 的问题 假如有两个树,一个是之前,一个是更变之后,我们抽象成两种可能性. 插入内容在最后 插入内容在最前 关于插在中间,原理一样,就不阐述. 使用 ul ...

  10. 基于HTML5 Canvas WebGL制作分离摩托车

    工业方面制作图表,制作模型方面运用到 3d 模型是非常多的,在一个大的环境中,构建无数个相同的或者不同的模型,构建起来对于程序员来说也是一件相当头疼的事情,我们利用 HT 帮大家解决了很大的难题,无数 ...