原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ33.html

题解

  首先我们把问题转化成处理一个数组 ans ,其中 ans[i] 表示 d(u,a) 和 d(v,a) 同时为 i 的倍数的 (u,v) 个数。(最后求答案的时候只要莫比乌斯反演回来就好了。)

  注意一下我的代码中对于 (u,v) 有祖先关系的是分开考虑的。

  先点分治。

  对于一个点分中心 x ,我们把答案分两部分考虑。

  1. 在子树 x 中满足 LCA(u,v) = x 的 (u,v) 对于答案的贡献。

  2. u,v 其中一个点在子树 x 中,另一个不在。

  第一部分非常好求,不加赘述。

  第二部分,我们考虑定义一个阀值 S ,我们预处理出 Smod[i][j] 表示 子树 x 中,到 x 的距离 mod i = j 的点的个数。这样,我们就可以 O(1) 得到 在子树 x 中,到达 x 的某一个祖先的距离为 i 的倍数的点的个数 。这样,我们就可以在 $O(nS)$ 的复杂度内求出对于 $ans[i](i\leq S)$ 的贡献。 对于 i>S 的,我们可以直接暴力计算 在子树 x 中,到达 x 的某一个祖先的距离为 i 的倍数的点的个数 ,复杂度为 $O(n^2/S)$ 。取 $S = O(\sqrt{n})$ 最优。故处理一个点分中心的复杂度为 $O(n\sqrt{n})$ (假设当前连通块大小为 n)。

  所以总的时间复杂度为 $O(n\sqrt{n})$ 。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=200005,M=500;
int n;
vector <int> e[N];
LL ans[N],ans2[N];
int depth[N],fa[N];
void dfs(int x,int pre,int d){
fa[x]=pre,depth[x]=d;
for (auto y : e[x])
if (y!=pre)
dfs(y,x,d+1);
}
int vis[N],size[N],Size;
int Maxsize[N],rt;
void get_root(int x,int pre){
size[x]=1,Maxsize[x]=0;
for (auto y : e[x])
if (y!=pre&&!vis[y]){
get_root(y,x);
size[x]+=size[y];
Maxsize[x]=max(Maxsize[x],size[y]);
}
Maxsize[x]=max(Maxsize[x],Size-size[x]);
if (!rt||Maxsize[rt]>Maxsize[x])
rt=x;
}
vector <int> d[N];
void get_size(int x,int pre){
size[x]=1;
for (auto y : e[x])
if (y!=pre&&!vis[y])
get_size(y,x),size[x]+=size[y];
}
void getd(int x,int pre,int d,vector <int> &v){
while (d>=(int)v.size())
v.push_back(0);
v[d]++;
for (auto y : e[x])
if (y!=pre&&!vis[y])
getd(y,x,d+1,v);
}
LL S[N];
LL Smod[M][M];
void solve(int x){
rt=0;
get_root(x,0);
assert(rt!=0);
vis[x=rt]=1;
for (int i=0;i<=Size;i++)
S[i]=0;
int Mx=0;
for (auto y : e[x])
if (!vis[y]){
get_size(y,0);
if (depth[y]<depth[x])
continue;
d[y].clear();
getd(y,0,1,d[y]);
int t=d[y].size()-1;
for (int i=1;i<=t;i++){
for (int j=i<<1;j<=t;j+=i)
d[y][i]+=d[y][j];
ans[i]+=(LL)d[y][i]*S[i];
S[i]+=d[y][i];
}
Mx=max(Mx,t);
d[y].clear();
}
for (int i=Mx;i>=1;i--)
for (int j=i<<1;j<=Mx;j+=i)
S[i]-=S[j];
S[0]++;
int base=(int)(0.4*sqrt(Mx)+0.5);
for (int i=1;i<=base;i++){
for (int j=0;j<i;j++)
Smod[i][j]=0;
for (int j=0;j<=Mx;j++)
Smod[i][j%i]+=S[j];
}
for (int f=fa[x],pre=x;f&&!vis[f];pre=f,f=fa[f]){
d[f].clear();
for (auto y : e[f])
if (!vis[y]&&y!=pre&&y!=fa[f])
getd(y,f,1,d[f]);
int t=d[f].size()-1;
for (int i=1;i<=t;i++){
for (int j=i<<1;j<=t;j+=i)
d[f][i]+=d[f][j];
int tmp=(i-(depth[x]-depth[f])%i)%i;
if (i<=base)
ans[i]+=(LL)d[f][i]*Smod[i][tmp];
else
for (int j=tmp;j<=Mx;j+=i)
ans[i]+=(LL)d[f][i]*S[j];
}
d[f].clear();
}
for (auto y : e[x])
if (!vis[y])
Size=size[y],solve(y);
}
int main(){
n=read();
for (int i=2;i<=n;i++){
int x=read();
e[i].push_back(x);
e[x].push_back(i);
}
dfs(1,0,0);
Size=n;
solve(1);
for (int i=n;i>=1;i--)
for (int j=i<<1;j<=n;j+=i)
ans[i]-=ans[j];
for (int i=1;i<=n;i++)
ans2[depth[i]]++;
for (int i=n;i>=1;i--)
ans2[i]+=ans2[i+1];
for (int i=1;i<n;i++)
printf("%lld\n",ans[i]+ans2[i]);
return 0;
}

  

UOJ#33. 【UR #2】树上GCD 点分治 莫比乌斯反演的更多相关文章

  1. 【CJOJ2512】gcd之和(莫比乌斯反演)

    [CJOJ2512]gcd之和(莫比乌斯反演) 题面 给定\(n,m(n,m<=10^7)\) 求 \[\sum_{i=1}^n\sum_{j=1}^mgcd(i,j)\] 题解 首先把公因数直 ...

  2. bnu——GCD SUM (莫比乌斯反演)

    题目:GCD SUM 题目链接:http://www.bnuoj.com/v3/problem_show.php?pid=39872 算法:莫比乌斯反演.优化 #include<stdio.h& ...

  3. 【Project Euler】530 GCD of Divisors 莫比乌斯反演

    [题目]GCD of Divisors [题意]给定f(n)=Σd|n gcd(d,n/d)的前缀和F(n),n=10^15. [算法]莫比乌斯反演 [题解]参考:任之洲数论函数.pdf 这个范围显然 ...

  4. GCD HDU - 1695 莫比乌斯反演入门

    题目链接:https://cn.vjudge.net/problem/HDU-1695#author=541607120101 感觉讲的很好的一个博客:https://www.cnblogs.com/ ...

  5. HDU - 4675 GCD of Sequence (莫比乌斯反演+组合数学)

    题意:给出序列[a1..aN],整数M和k,求对1-M中的每个整数d,构建新的序列[b1...bN],使其满足: 1. \(1 \le bi \le M\) 2. \(gcd(b 1, b 2, -, ...

  6. [luogu P2586] GCD 解题报告 (莫比乌斯反演|欧拉函数)

    题目链接:https://www.luogu.org/problemnew/show/P2568#sub 题目大意: 计算​$\sum_{x=1}^n\sum_{y=1}^n [gcd(x,y)==p ...

  7. 【HDU4947】GCD Array(莫比乌斯反演+树状数组)

    点此看题面 大致题意: 一个长度为\(n\)的数组,实现两种操作:将满足\(gcd(i,k)=d\)的\(a_i\)加上\(v\),询问\(\sum_{i=1}^xa_i\). 对于修改操作的推式子 ...

  8. 数学--数论--HDU 4675 GCD of Sequence(莫比乌斯反演+卢卡斯定理求组合数+乘法逆元+快速幂取模)

    先放知识点: 莫比乌斯反演 卢卡斯定理求组合数 乘法逆元 快速幂取模 GCD of Sequence Alice is playing a game with Bob. Alice shows N i ...

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

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

随机推荐

  1. 修改SIP协议中的User-Agent名称

    修改目的:如果user-agent 带上了 GIT 版本信息,容易被人抓住版本漏洞针对性的攻击. 示例如下: SIP/2.0 100 Trying Via: SIP/2.0/UDP 192.168.5 ...

  2. 我的大学,我的SPR机器人队

    时间过的真快,我这个在协会呆了好多年的老油条今年都毕业了,在石油大学大学七年几乎三分之二的时间就是在协会度过的.实话说在北京这是我最亲切的地方,这里有我喜欢的各种设备,有亲爱的老师和一起奋斗的队友,在 ...

  3. ASP.NET Core之NLog使用

    1.新建ASP.NET Core项目 1.1选择项目 1.2选择.Net版本 2. 添加NLog插件 2.1 通过Nuget安装 2.2下载相关的插件 3.修改NLog配置文件 3.1添加NLog配置 ...

  4. python学习第39天

    # 数据操作 # 增 # 删 # 改 # 查 # 单表查询 # 多表查询

  5. 最新手机号码验证正则表达式(PHP版本)

    1 前言 手机号码是否合规,则需要校验,可以使用正则表达式. 2 代码 function checkPhoneNumberValidate($phone_number){ //@2017-11-25 ...

  6. CentOS 7 服务器之间ssh无密码登录、传输文件

    在Linux服务器之间使用ssh命令向另一个Linux服务器发送执行指令是需要输入密码     ssh登录提供两种认证方式:口令(密码)认证方式和密钥认证方式.其中口令(密码)认证方式是我们最常用的一 ...

  7. 【Java】「深入理解Java虚拟机」学习笔记(4)- 类文件结构

    我为什么喜欢Java,另一个重要原因就是跨平台,WORA. 程序员是爽了,但肯定有人要为你遮风挡雨,解决WORA的基石就是字节码+虚拟机. ♣Tip 其实这里存在两种无关性,一是平台无关性.另一个是语 ...

  8. ActiveMQ消息的发送原理

    持久化消息和非持久化消息的发送策略:消息同步发送和异步发送 ActiveMQ支持同步.异步两种发送模式将消息发送到broker上.同步发送过程中,发送者发送一条消息会阻塞直到broker反馈一个确认消 ...

  9. 【python】升级pip后报错解决pkg_resources.DistributionNotFound: The 'pip==7.1.0' distribution was not found and is required by the application

    原本使用pip版本为7.1.0,后升级至9.0.1 之后使用pip list提示pkg_resources.DistributionNotFound: The 'pip==7.1.0' distrib ...

  10. lightoj1214 大数取模模板

    #include<bits/stdc++.h> using namespace std; #define maxn 300 #define ll long long ll a,b; ]; ...