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的层层递进的思路的伟大之处,作为一个大 ...
随机推荐
- Java:内省(Introspector)
内省(Introspector) 是Java 语言对 JavaBean 类属性.事件的一种缺省处理方法. JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且 ...
- Linux系列教程(五)——Linux常用命令之链接命令和权限管理命令
前一篇博客我们讲解了Linux文件和目录处理命令,还是老生常淡,对于新手而言,我们不需要完全记住命令的详细语法,记住该命令能完成什么功能,然后需要的时候去查就好了,用的多了我们就自然记住了.这篇博客我 ...
- CF888G Xor-MST 生成树、分治、Trie树合并
传送门 第一次接触到Boruvka求最小生成树 它的原版本是:初始每一个点构成一个连通块,每一次找到每一个连通块到其他的连通块权值最短的边,然后合并这两个连通块.因为每一次连通块个数至少减半,所以复杂 ...
- ASP.NET Core 登录失败。该登录名来自不受信任的域,不能与集成身份验证一起使用。
原文:ASP.NET Core 登录失败.该登录名来自不受信任的域,不能与集成身份验证一起使用. 当进行数据迁移的时候提示 修改appsettings配置连接串的Trusted_Connection ...
- bitcoin 源码解析 - 交易 Transaction(二) - 原理篇
这篇文章我断断续续写了呃···· 应该快三个星期了? 所以前后的风格可能差别相当大.真是十分的怠惰啊··· 最近实在是不够努力.用python重写bitcoin的项目也卡在网络编程部分(这方面真是我的 ...
- WPF开发汽车采样机上位机软件
由于项目需要,需开发同一套汽车.火车.皮带采样机的上位机软件. 看过之前的上位机软件,老版本都是DelPhi.VB开发,稍微新语言开发的是采用winform开发.要不就是使用组态软件. Delphi语 ...
- VMware workstation运维实践系列博客导航
第一章:VMware workstation虚拟化1.1 VMware workstation计算网络存储介绍1.2 VMware workstation其他功能特性介绍1.3 VMware work ...
- Vue Element Tabe Pager 分页方案
表格和分页分离的,但是使用中,却是结合在一起的. 分析 有以下方式触发查询: mounted 加载数据. 查询按钮 加载数据. pager 变化加载数据 加载数据函数: loadData 问题 mou ...
- 分布式监控系统Zabbix--完整安装记录-批量添加主机和自动发现端口
一.Zabbix-3.0.3批量添加主机的配置如下: 0)被监控机上要安装zabbix_agent,并配置好zabbix_agentd.conf (如下172.29.8.50是zabbix_serve ...
- PAT甲题题解-1130. Infix Expression (25)-中序遍历
博主欢迎转载,但请给出本文链接,我尊重你,你尊重我,谢谢~http://www.cnblogs.com/chenxiwenruo/p/6789828.html特别不喜欢那些随便转载别人的原创文章又不给 ...