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的层层递进的思路的伟大之处,作为一个大 ...
随机推荐
- go get 碰壁怎么办?
如果要让go get顺利进行,必须注意2个问题: 1.墙:2.墙: 解决办法是安装和配置shadowsocks和polipo.shadowsocks是socks5协议,polipo是将sock ...
- ASP.NET Core StaticFiles中间件修改wwwroot(转载)
ASP.NET Core 开发,中间件(StaticFiles)的使用,我们开发一款简易的静态文件服务器.告别需要使用文件,又需要安装一个web服务器.现在随时随地打开程序即可使用,跨平台,方便快捷. ...
- C#的抽象类别
抽象类,有3个特点: 第1个,不能被实例化. 第2个,类中的抽象方法在继承的子类中必须重写. 第3个,类一旦有抽象方法出现,那这个类必须定义为抽象类. 现实开发中,发现有共同的代码,可以把这些共同的代 ...
- ng-include文件实现ng-repeat
Angularjs实现自由度很高.比如ng-repeat可以以包含的文件中实现数据循环. 如: 当我们把这html文件被ng-include包含时,它完全能正常呈现对应的数据: 创建应用app: 创建 ...
- ListView 控件和 INotifyPropertyChanged 接口
原文:ListView 控件和 INotifyPropertyChanged 接口 ListView 控件和 DataGridView 控件 ListView 是跟 Winform 中 DataGri ...
- MVC4程序运行报错
近期了解MVC4的时候弄了一个简单的小工程,使用Entity Framework作为Model,F5启动调试运行的时候没有问题,但是发布到IIS之后访问就报错 错误信息如下: The Entity F ...
- POJ1850&&1019&&1942
这三道题都水的难以想象,所以就放在一起写 1850 题目大意:有一种一种序列排列方式(如同题目中给出的例子),然后给你一个序列,问你这个序列的排名 首先我们先判断无解的情况,这个就很简单了. 由于题目 ...
- 基于uFUN开发板的心率计(一)DMA方式获取传感器数据
前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...
- 快速零配置迁移 API 适配 iOS 对 IPv6 以及 HTTPS 的要求
本文快速分享一下快速零配置迁移 API 适配 iOS 对 IPv6 以及 HTTPS 的要求的方法,供大家参考. 原文发表于我的技术博客 零配置方案 最新的苹果审核政策对 API 的 IPv6 以及 ...
- proxy_pass反向代理配置中url后面加不加/的说明
在日常的web网站部署中,经常会用到nginx的proxy_pass反向代理,有一个配置需要弄清楚:配置proxy_pass时,当在后面的url加上了/,相当于是绝对根路径,则nginx不会把loca ...