UOJ33 [UR #2] 树上GCD 【点分治】【容斥原理】【分块】
题目分析:
树上点对问题首先想到点分治。假设我们进行了点分治并递归地解决了子问题。现在我们合并问题。
我们需要找到所有经过当前重心$ 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 【点分治】【容斥原理】【分块】的更多相关文章
- 【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 点分治 莫比乌斯反演
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ33.html 题解 首先我们把问题转化成处理一个数组 ans ,其中 ans[i] 表示 d(u,a) 和 ...
- 【uoj33】 UR #2—树上GCD
http://uoj.ac/problem/33 (题目链接) 题意 给出一棵${n}$个节点的有根树,${f_{u,v}=gcd(dis(u,lca(u,v)),dis(v,lca(u,v)))}$ ...
- [UOJ UR #2]树上GCD
来自FallDream的博客,未经允许,请勿转载,谢谢. 传送门 看完题目,一般人都能想到 容斥稳了 .这样我们只要统计有多少点对满足gcd是i的倍数. 考虑长链剖分,每次合并的时候,假设我已经求出轻 ...
- 【UOJ#33】【UR #2】树上GCD(长链剖分,分块)
[UOJ#33][UR #2]树上GCD(长链剖分,分块) 题面 UOJ 题解 首先不求恰好,改为求\(i\)的倍数的个数,最后容斥一下就可以解决了. 那么我们考虑枚举一个\(LCA\)位置,在其两棵 ...
- poj1741 树上的点分治
题意: 一棵10000个点的树,每条边的长不超过1000,给定一个值k,问距离不超过k的点对数有多少.(多组数据) 输入样例: 5 4 1 2 3 1 3 1 1 4 2 3 5 1 0 0输出样例: ...
- 【UOJ#50】【UR #3】链式反应(分治FFT,动态规划)
[UOJ#50][UR #3]链式反应(分治FFT,动态规划) 题面 UOJ 题解 首先把题目意思捋一捋,大概就是有\(n\)个节点的一棵树,父亲的编号大于儿子. 满足一个点的儿子有\(2+c\)个, ...
- [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)有多少对. ...
- 还不会做! 树上的gcd 树分治 UOJ33
题目链接:http://uoj.ac/problem/33 题解链接:http://vfleaking.blog.uoj.ac/blog/38 现在感觉到了做OI的层层递进的思路的伟大之处,作为一个大 ...
随机推荐
- IIC双向电平转换电路设计
现代的集成电路工艺加工的间隙可达0.5μm 而且很少限制数字I/O 信号的最大电源电压和逻辑电平. 为了将这些低电压电路与已有的5V或其他I/O电压器件连接起来,接口需要一个电平转换器.对于双向的总线 ...
- 使用jdom进行xml解析,网络抓包
最近再做一个项目,使用到了jdom进行xml解析,为了方便记忆,现在保存在这里 package bboss; import java.io.FileInputStream; import java.i ...
- CF28D Don't fear, DravDe is kind 背包
题目传送门:http://codeforces.com/problemset/problem/28/D 题意:给你$N$个物品,每个物品有其价格$P_i$,之前必须要买的物品价格和$L_i$,之后必须 ...
- 浅谈左偏树在OI中的应用
Preface 可并堆,一个听起来很NB的数据结构,实际上比一般的堆就多了一个合并的操作. 考虑一般的堆合并时,当我们合并时只能暴力把一个堆里的元素一个一个插入另一个堆里,这样复杂度将达到\(\log ...
- AT3611 Tree MST
题面 题解 考虑最小化\(dis(x, y)\) 这里需要对一种奇怪的最小生成树算法:Boruvka算法有深刻的理解. 考虑该算法的执行过程,我们可以考虑进行点分治,每次找到离分治重心最近的点,然后将 ...
- QT 遇到的问题
遇到的问题: 1:在QT中使用opengl,发现一个很神奇的问题,个人感觉是qt的bug. 问题详情:在我添加了一个成员变量之后,使用opengl编写的窗口没有任何输出了,只有一个背景. 但是删除那个 ...
- js类型----你所不知道的JavaScript系列(5)
ECMAScirpt 变量有两种不同的数据类型:基本类型,引用类型.也有其他的叫法,比如原始类型和对象类型等. 1.内置类型 JavaScript 有七种内置类型: • 空值(null) • 未定义( ...
- django通用权限控制框架
在web项目中根据不同的用户肯定会限制其不同的权限,利用以下模块可以满足日常几乎所有的权限控制 permission_hook.py # 自定义权限控制,必须返回True/false ,True表 ...
- 点评qq浏览器
1.内核. qq浏览器用的是是IE8的内核,而且是只有IE内核,所以,在速度上没办法跟那些webkit内核做对比了,不过也没有太慢,在沈航的网速下,打开网页的速度也还是勉强可以接受的. ...
- 使用Java+Kotlin双语言的LeetCode刷题之路(一)
LeetCode learning records based on Java,Kotlin,Python...Github 地址 序号对应 LeetCode 中题目序号 1 两数之和 给定一个整数数 ...