序列变换
题目描述
\(lyk\) 有两序列 \(a\) 和 \(b\)。
\(lyk\) 想知道存在多少对 \(x,y\),满足以下两个条件。
\(1:\gcd(x,y)=1\)。
\(2:a_{b_x} = b_{a_y}\)。
例如若 \(a={1,1,1},b={1,1,1}\)。那么存在 \(7\) 对,因为除了 \(x=2,y=2\) 或 \(x=3,y=3\) 外都满足条件。
输入
第一行一个数 \(n(1<=n<=100000)\)。
接下来一行n个数,表示 \(ai(1<=ai<=n)\)。
接下来一行n个数,表示 \(bi(1<=bi<=n)\)。
输出
一行表示答案
输入样例
3
1 1 1
1 1 1
输出样例
7
解析
这是我第一次用莫比乌斯反演性质二的第一题
先打出莫比乌斯反演性质二:
有数论函数 \(f(n)= \sum_{d|n} g(d)\)
逆求 \(g(n)\)
则有:
\[ g(n)=\sum_{d|n} f(d) \mu(\frac{n}{d}) \]
日后再证
然后套路解题
设
\[ f(x) = \sum_{i=1}^n \sum_{j=1}^n [\gcd(i,j)=x][a_{b_x} = b_{a_y}] \]
再设
\[ F(x)=\sum_{x|d} f(d) \]
然后玩第二个式子
\[ \begin{aligned} F(x) &=\sum_{x|d} f(d) \\ &=\sum_{x|d} \sum_{i=1}^n \sum_{j=1}^n [\gcd(i,j)=d][a_{b_x} = b_{a_y}] \\ &=\sum_{i=1}^n \sum_{j=1}^n [x|gcd(i,j)][a_{b_x} = b_{a_y}] \\ &=\sum_{x|i} \sum_{x|j} [a_{b_x} = b_{a_y}] \end{aligned} \]
那么,答案就是:
\[ f(1) = \sum_{i=1}^n F(i) \mu(i) \]
求 \(f(1)\) 我们期望是 \(O(n)\)
所以我们要预处理出 \(\mu\) 和所有 \(F(i)\)
如何快速计算 \(F(i)\) ?
呵呵,先开个桶,然后~~~
看我代码吧,政治觉悟略高的同志必然能看得懂
代码
#include<cstdio>
using namespace std;
typedef long long LL;
const int N = 1e5;
int a[N + 5] , b[N + 5] , n , mu[N + 5] , tot , prime[N + 5] , vis[N + 5];
LL cnt[N + 5] , F[N + 5] , ans;
inline void getF()
{
for(register int i = 1; i <=n; i++)
{
for(register int j = i; j <= n; j += i) cnt[a[b[j]]]++;
for(register int j = i; j <= n; j += i) F[i] += cnt[b[a[j]]];
for(register int j = i; j <= n; j += i) cnt[a[b[j]]]--;
}
}
inline void getmu()
{
mu[1] = 1;
for(register int i = 2; i <=n; i++)
{
if (!vis[i]) mu[prime[++tot] = i] = -1;
for(register int j = 1; j <= tot && prime[j] * i <= n; j++)
{
vis[prime[j] * i] = 1;
if (i % prime[j] == 0) break;
mu[prime[j] * i] = -mu[i];
}
}
}
int main()
{
scanf("%d" , &n);
for(register int i = 1; i <= n; i++) scanf("%d" , &a[i]);
for(register int i = 1; i <= n; i++) scanf("%d" , &b[i]);
getF() , getmu();
for(register int i = 1; i <= n; i++) ans += (LL)mu[i] * F[i];
printf("%lld" , ans);
}