传送门

简化题意:给出一棵\(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. loadrunner 运行脚本-Run-time Settings之Preferences设置

    运行脚本-Run-time Settings之Preferences设置 by:授客 QQ:1033553122 打开Preferences设置对话框,这里提供了对运行时的参数选择设置 Enable ...

  2. Linux 目录结构学习与简析 Part1

    linux目录结构学习与简析 by:授客 QQ:1033553122 说明: /             linux系统目录树的起点 =============== /bin      User Bi ...

  3. Django--数据库查询操作

    MySQL是几乎每一个项目都会使用的一个关系数据库,又因为它是开源免费的,所以很多企业都用它来作为自家后台的数据库. BAT这类大公司除外,它们的业务数据是以亿级别来讨论的,而MySQL的单表6000 ...

  4. GridView的簡單使用

    項目GitHub地址:https://github.com/leonInShanghai/IMbobo GridView XML佈局: <?xml version="1.0" ...

  5. IE push方法,最后一个参数后面不能跟",",否则报语法错误

    var columns = [[]]; columns[0].push( { field: 'ADDNAME', title: '添加人', width: 80, }, { field: 'ADDDT ...

  6. 一文读懂遗传算法工作原理(附Python实现)

    选自AnalyticsVidhya 参与:晏奇.黄小天 近日,Analyticsvidhya 上发表了一篇题为<Introduction to Genetic Algorithm & t ...

  7. 系统运维|IIS的日志设置

    摘要: 1.服务器告警,磁盘资源不足 2.检查发现是IIS日志没有清理并且设置有误.在E盘占用了200G的空间 3.原则上IIS日志不能放在C盘,避免C盘写满了导致操作系统异常 4.附上IIS日志按天 ...

  8. January 11th, 2018 Week 02nd Thursday

    Live, travel, adventure, bless, and don't be sorry. 精彩地活着,不停地前行,大胆冒险,心怀感激,不留遗憾. Everything we do is ...

  9. 【ReactNative】Mac下分分钟打包 Android apk

    时间:2016-11-20 09:17:07 地址:https://github.com/zhongxia245/blog/issues/52 Mac 下 ReactNative如何打包构建Andro ...

  10. 个人技术博客(α)------javaweb的学习路程

    该博文大致内容是学习的一个过程,心得,并不是以技术博客为主,在此说明. 关于javaweb的学习开始的时间大概是从大二下(2017年6.7月份)的暑假开始的,在学长的介绍下加入了实验室进行学习,由于是 ...