传送门

简化题意:给出一棵\(n\)个点的树,编号为\(1\)到\(n\),第\(i\)个点的点权为\(a_i\),保证序列\(a_i\)是一个\(1\)到\(n\)的排列,求

\[\frac{1}{n(n-1)} \sum\limits_{i=1}^n \sum\limits_{j=1}^n \varphi(a_ia_j) dist(i,j)
\]

其中\(dist(i,j)\)为树上\(i,j\)两点的距离。


看到\(\varphi\)第一反应推式子

因为序列\(a_i\)是一个\(1\)到\(n\)的排列,设\(t_i\)表示点权为\(i\)的点的编号,那么原式等于$ \frac{1}{n(n-1)} \sum\limits_{i=1}^n \sum\limits_{j=1}^n \varphi(ij) dist(t_i,t_j)$

接下来化简$\sum\limits_{i=1}^n \sum\limits_{j=1}^n\varphi(ij) $

考虑\(gcd(i,j)\)在\(\varphi(ij)\)中的贡献,不难得到\(\varphi(ij) = \frac{\varphi(i) \varphi(j) gcd(i,j)}{\varphi(gcd(i,j))}\)

代入得$ \sum\limits_{i=1}^n \sum\limits_{j=1}^n \varphi(ij) =\sum\limits_{i=1}^n \sum\limits_{j=1}^n \frac{\varphi(i) \varphi(j) gcd(i,j)}{\varphi(gcd(i,j))}$

按照套路枚举\(gcd\):\(=\sum\limits_{d=1}^n \frac{d}{\varphi(d)} \sum\limits_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{n}{d} \rfloor} \varphi(id) \varphi(jd) [gcd(i,j) == 1]=\sum\limits_{d=1}^n \frac{d}{\varphi(d)} \sum\limits_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{n}{d} \rfloor} \varphi(id) \varphi(jd) \sum\limits_{p | gcd(i,j)} \mu(p)\)

将\(p\)移到前面:\(=\sum\limits_{d=1}^n \sum\limits_{p=1}^{\lfloor \frac{n}{d} \rfloor} \frac{d \mu(p)}{\varphi(d)} \sum\limits_{i=1}^{\lfloor \frac{n}{dp} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{n}{dp} \rfloor} \varphi(idp) \varphi(jdp)\)

\(dp\)太难看了考虑枚举\(T=dp\):\(=\sum\limits_{T=1}^n \sum\limits_{d | T} \frac{d \mu(\frac{T}{d})}{\varphi(d)} \sum\limits_{i=1}^{\lfloor \frac{n}{T} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{n}{T} \rfloor} \varphi(iT) \varphi(jT)\)

然后将\(dist(t_i,t_j)\)代回来。注意这个时候\(i,j\)的意义发生了变化,我们应该要代入的是\(dist(t_{iT} , t_{jT})\)

所以我们需要求的是\(=\sum\limits_{T=1}^n \sum\limits_{d | T} \frac{d \mu(\frac{T}{d})}{\varphi(d)} \sum\limits_{i=1}^{\lfloor \frac{n}{T} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{n}{T} \rfloor} \varphi(iT) \varphi(jT) dist(t_{iT} , t_{jT})\)

推到这里我们就可以做了,相对来说还是比较好推的……

对于\(\sum\limits_{d | T} \frac{d \mu(\frac{T}{d})}{\varphi(d)}\),枚举倍数做到\(O(nlogn)\)预处理

对于\(\sum\limits_{i=1}^{\lfloor \frac{n}{T} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{n}{T} \rfloor} \varphi(iT) \varphi(jT) dist(t_{iT} , t_{jT})\),它等于\(\sum\limits_{i=1}^{\lfloor \frac{n}{T} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{n}{T} \rfloor} \varphi(iT) \varphi(jT) (dep_{t_{iT}} + dep_{t_{jT}} - 2 * dep_{LCA(t_{iT} , t_{jT})})\)。我们把所有点权为\(T\)的倍数的点拿出来建立虚树进行树形DP,对于每一个点维护它子树中满足条件的点的\(\sum \varphi(i) \times dep_i\)与\(\sum \varphi(i)\),在两个子树合并时计算答案即可。总复杂度约为\(O(nlog^2n)\)。

最后记录一些被坑的细节(大概只有我这种菜鸡才会犯……)

\(1.\)建立虚树的点不是编号为\(T\)的倍数的点,而是点权是\(T\)的倍数的点

\(2.\)\(\frac{d}{\varphi(d)}\)不一定是整数

\(3.\)写程序的时候要保持清醒……发现很多地方\(i\)打成\(j\),\(a_x\)打成\(x\)之类的……

\(4.\)记得清空数组

#include<bits/stdc++.h>
//This code is written by Itst
using namespace std; inline int read(){
int a = 0;
char c = getchar();
bool f = 0;
while(!isdigit(c) && c != EOF){
if(c == '-')
f = 1;
c = getchar();
}
if(c == EOF)
exit(0);
while(isdigit(c)){
a = a * 10 + c - 48;
c = getchar();
}
return f ? -a : a;
} const int MAXN = 2e5 + 9 , MOD = 1e9 + 7;
int mu[MAXN] , phi[MAXN] , prime[MAXN] , cnt;
bool nprime[MAXN];
struct Edge{
int end , upEd;
}Ed[MAXN << 1];
int N , cntEd , ts , top , cntT , cntST , cur , ans1 , ans;
int head[MAXN] , val[MAXN] , ind[MAXN];
int dfn[MAXN] , dep[MAXN] , fir[MAXN] , ST[21][MAXN << 1] , logg2[MAXN << 1];
int st[MAXN] , tree[MAXN] , dp1[MAXN] , dp2[MAXN] , q[MAXN];
vector < int > ch[MAXN]; inline void addEd(int a , int b){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
head[a] = cntEd;
} void input(){
N = read();
for(int i = 1 ; i <= N ; ++i)
ind[val[i] = read()] = i;
for(int i = 1 ; i < N ; ++i){
int a = read() , b = read();
addEd(a , b);
addEd(b , a);
}
} inline void init(){
phi[1] = mu[1] = 1;
for(int i = 2 ; i <= N ; ++i){
if(!nprime[i]){
prime[++cnt] = i;
phi[i] = i - 1;
mu[i] = -1;
}
for(int j = 1 ; j <= cnt && prime[j] * i <= N ; ++j){
nprime[i * prime[j]] = 1;
if(i % prime[j] == 0){
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
mu[i * prime[j]] = mu[i] * -1;
}
}
} void init_dfs(int x , int p){
dfn[x] = ++ts;
ST[0][++cntST] = x;
fir[x] = cntST;
dep[x] = dep[p] + 1;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(Ed[i].end != p){
init_dfs(Ed[i].end , x);
ST[0][++cntST] = x;
}
} inline int cmp(int a , int b){
return dep[a] < dep[b] ? a : b;
} void init_ST(){
for(int i = 2 ; i <= cntST ; ++i)
logg2[i] = logg2[i >> 1] + 1;
for(int i = 1 ; 1 << i <= cntST ; ++i)
for(int j = 1 ; j + (1 << i) <= cntST + 1 ; ++j)
ST[i][j] = cmp(ST[i - 1][j] , ST[i - 1][j + (1 << (i - 1))]);
} inline int LCA(int x , int y){
x = fir[x];
y = fir[y];
if(x > y)
swap(x , y);
int t = logg2[y - x + 1];
return cmp(ST[t][x] , ST[t][y - (1 << t) + 1]);
} inline int poww(long long a , int b){
int times = 1;
while(b){
if(b & 1)
times = times * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return times;
} bool cmp1(int a , int b){
return dfn[a] < dfn[b];
} int dfs(int x){
dp1[x] = dp2[x] = 0;
int sum = 0;
for(int i = 0 ; i < ch[x].size() ; ++i){
sum = (sum + dfs(ch[x][i])) % MOD;
sum = (sum + 1ll * dp1[x] * dp2[ch[x][i]] % MOD + 1ll * dp1[ch[x][i]] * dp2[x] % MOD - 2ll * dep[x] * dp1[x] % MOD * dp1[ch[x][i]] % MOD + MOD) % MOD;
dp1[x] = (dp1[x] + dp1[ch[x][i]]) % MOD;
dp2[x] = (dp2[x] + dp2[ch[x][i]]) % MOD;
}
if(val[x] % cur == 0){
sum = (sum + 1ll * dp2[x] * phi[val[x]] % MOD - 1ll * dep[x] * dp1[x] % MOD * phi[val[x]] % MOD + MOD) % MOD;
dp1[x] = (dp1[x] + phi[val[x]]) % MOD;
dp2[x] = (dp2[x] + 1ll * phi[val[x]] * dep[x]) % MOD;
}
return sum;
} inline void calc(int x){
cntT = 0;
for(int i = 1 ; x * i <= N ; ++i){
ch[ind[x * i]].clear();
tree[++cntT] = ind[x * i];
}
sort(tree + 1 , tree + cntT + 1 , cmp1);
for(int i = 1 ; i <= cntT ; ++i){
if(top){
int t = LCA(st[top] , tree[i]);
while(top - 1 && dep[st[top - 1]] >= dep[t]){
ch[st[top - 1]].push_back(st[top]);
--top;
}
if(t != st[top]){
ch[t].clear();
ch[t].push_back(st[top]);
st[top] = t;
}
}
st[++top] = tree[i];
}
int rt = st[1];
while(top > 1){
ch[st[top - 1]].push_back(st[top]);
--top;
}
top = 0;
ans = (ans + 1ll * q[x] * dfs(rt)) % MOD;
} int main(){
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
//freopen("out","w",stdout);
#endif
input();
init();
init_dfs(1 , 0);
init_ST();
for(int i = 1 ; i <= N ; ++i)
for(int j = 1 ; j * i <= N ; ++j)
q[i * j] = (q[i * j] + 1ll * i * mu[j] * poww(phi[i] , MOD - 2) % MOD + MOD) % MOD;
for(cur = 1 ; cur <= N / 2 ; ++cur)
calc(cur);
cout << 2ll * ans * poww(1ll * N * (N - 1) % MOD , MOD - 2) % MOD;
return 0;
}

CF809E Surprise me! 莫比乌斯反演、虚树的更多相关文章

  1. Codeforces.809E.Surprise me!(莫比乌斯反演 虚树)

    题目链接 \(Description\) 给定一棵树,求\[\frac{1}{n(n-1)/2}\times\sum_{i\in[1,n],j\in[1,n],i\neq j}\varphi(a_i\ ...

  2. 【Codeforces 809E】Surprise me!(莫比乌斯反演 & 虚树)

    Description 给定一颗 \(n\) 个顶点的树,顶点 \(i\) 的权值为 \(a_i\).求: \[\frac{1}{n(n-1)}\sum_{i=1}^n\sum_{j=1}^n\var ...

  3. YbtOJ#723-欧拉之树【莫比乌斯反演,虚树】

    正题 题目链接:http://www.ybtoj.com.cn/contest/121/problem/2 题目大意 给出\(n\)个点的一棵树,每个点有一个权值\(a_i\),求 \[\sum_{i ...

  4. 【BZOJ3529】数表(莫比乌斯反演,树状数组)

    [BZOJ3529]数表(莫比乌斯反演,树状数组) 题解 首先不管\(A\)的范围的限制 要求的东西是 \[\sum_{i=1}^n\sum_{j=1}^m\sigma(gcd(i,j))\] 其中\ ...

  5. 【bzoj3529】[Sdoi2014]数表 莫比乌斯反演+离线+树状数组

    题目描述 有一张n×m的数表,其第i行第j列(1 <= i <= n ,1 <= j <= m)的数值为能同时整除i和j的所有自然数之和.给定a,计算数表中不大于a的数之和. ...

  6. 【CF809E】Surprise me! 树形DP 虚树 数学

    题目大意 给你一棵\(n\)个点的树,每个点有权值\(a_i\),\(a\)为一个排列,求 \[ \frac{1}{n(n-1)}\sum_{i=1}^n\sum_{j=1}^n \varphi(a_ ...

  7. Codeforces 809E Surprise me! [莫比乌斯反演]

    洛谷 Codeforces 非常套路的一道题,很适合我在陷入低谷时提升信心-- 思路 显然我们需要大力推式子. 设\(p_{a_i}=i\),则有 \[ \begin{align*} n(n-1)an ...

  8. BZOJ3529: [Sdoi2014]数表 莫比乌斯反演_树状数组

    Code: #include <cstdio> #include <algorithm> #include <cstring> #define ll long lo ...

  9. 【CF809E】Surprise me!(动态规划,虚树,莫比乌斯反演)

    [CF809E]Surprise me!(动态规划,虚树,莫比乌斯反演) 题面 洛谷 CodeForces 翻译: 给定一棵\(n\)个节点的树,每个点有一个权值\(a[i]\),保证\(a[i]\) ...

随机推荐

  1. PHP与.Net的区别(一)接口

    一.关于接口成员 PHP的接口成员只能包括两种: 1.函数签名 2.常量 .Net的接口成员只能包括三种: 1.函数签名 2.属性(注意:是属性,不是字段) 3.事件 4.索引器(也叫有参属性)

  2. 可视化接口管理工具RAP,模拟数据,校验接口

    最近看到一个不错的接口管理的工具,分享一下 RAP ppt介绍:http://www.imooc.com/video/11060 RAP是一个可视化接口管理工具 通过分析接口结构,动态生成模拟数据,校 ...

  3. Android 开发学习笔记

    1.Genymotion 解决虚拟镜像下载速度特别慢的问题 http://blog.csdn.net/qing666888/article/details/51622762 2.

  4. sql server 运维时CPU,内存,操作系统等信息查询(用sql语句)

    我们只要用到数据库,一般会遇到数据库运维方面的事情,需要我们寻找原因,有很多是关乎处理器(CPU).内存(Memory).磁盘(Disk)以及操作系统的,这时我们就需要查询他们的一些设置和内容,下面讲 ...

  5. Android ListView在增加HeaderView之后使用getLocationInWindow和getLocationOnScreen获得值不正确的解决方法

    近日遇到一个很恶心的问题,把解决方法放到空间里来分享给大家: 问题发生的条件: 1)ListView 控件中使用addHeaderView,为其添加了一个header view.(基本常识:heade ...

  6. Django学习---快速搭建搜索引擎(haystack + whoosh + jieba)

    Django下的搜索引擎(haystack + whoosh + jieba) 软件安装 haystack是django的开源搜索框架,该框架支持Solr,Elasticsearch,Whoosh, ...

  7. Centos7系统如何不重启系统识别新添加的硬盘?

    今天在系统开机后插入三块硬盘,结果没有一块硬盘被系统识别到.后来找到了方法. echo "- - -" > /sys/class/scsi_host/host0/scan 上 ...

  8. MyBatis实现模糊查询的几种方式

    在学习MyBatis过程中想实现模糊查询,可惜失败了.后来上百度上查了一下,算是解决了.记录一下MyBatis实现模糊查询的几种方式. 数据库表名为test_student,初始化了几条记录,如图: ...

  9. 为什么MySQL数据库索引选择使用B+树?

    在进一步分析为什么MySQL数据库索引选择使用B+树之前,我相信很多小伙伴对数据结构中的树还是有些许模糊的,因此我们由浅入深一步步探讨树的演进过程,在一步步引出B树以及为什么MySQL数据库索引选择使 ...

  10. Kubernetes中的Taint污点和Toleration容忍

    Taint(污点)和 Toleration(容忍)可以作用于 node 和 pod master 上添加taint kubectl taint nodes master1 node-role.kube ...