51nod 欢乐手速场1(迎新春)-B序列变换(莫比乌斯反演)

记录一个菜逼的成长。。

题目链接
序列变换
alpq654321 (命题人)
基准时间限制:1 秒 空间限制:131072 KB 分值: 40
lyk有两序列a和b。
lyk想知道存在多少对x,y,满足以下两个条件。
1: gcd(x,y)=1
2: abx=bay

例如若a={1,1,1},b={1,1,1}。那么存在7对,因为除了x=2,y=2或x=3,y=3外都满足条件。
Input

第一行一个数n(1<=n<=100000)。
接下来一行n个数,表示ai(1<=ai<=n)。
接下来一行n个数,表示bi(1<=bi<=n)。

Output

一行表示答案

Input示例

3
1 1 1
1 1 1

Output示例

7

官方题解:
如果没有gcd(x,y)=1的限制。
那么我们可以直接将所有b(ax)全部存进一个数组里。
然后将所有b(ay)出现的次数统计进答案里就可以了。
问题的关键是怎么处理这个限制。
我们可以考虑容斥。
即先把所有解都累加进答案,然后去掉gcd(x,y)!=1的解。
这里我们可以用莫比乌斯函数。
令设x为i在质因数分解中数的个数,y为i质因数分解后是否存在相同质因数。
那么若y=1则u[i]=0;否则
若x%2=1则u[i]=-1;否则
u[i]=1。
注意这里u[1]=1。
接下来我们只要计算出x,y都为t的倍数时的答案,再与u[t]相乘累加进答案就可以了。
总复杂度nlgn。

个人感觉官方题解讲的好复杂的样子。
我感觉就是莫比乌斯反演
假设,以下x,y都满足第二个条件
f(k) 表示gcd(x,y) == k的个数
F(k) 表示gcd(x,y) == k的倍数 的个数

然后就是 f(k)=nd=1μ(d)F(d)
这题F(d)不能直接算了,需要枚举k的倍数,用个数组统计满足第二个条件的个数

#include <bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
typedef long long LL;
const int maxn = 100000 + 10;
bool check[maxn];
int mu[maxn],prime[maxn];
void Mobius()
{
    cl(check,false);
    mu[1] = 1;
    int tot = 0;
    for( int i = 2; i < maxn; i++ ){
        if(!check[i]){
            prime[tot++] = i;
            mu[i] = -1;
        }
        for( int j = 0; j < tot; j++ ){
            if(i * prime[j] >= maxn)break;
            check[i*prime[j]] = true;
            if(i % prime[j] == 0){
                mu[i * prime[j]] = 0;
                break;
            }
            else {
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
}
int a[maxn],b[maxn],vis[maxn],n;
LL cal(int t)
{
    LL ret = 0;
    for( int i = t; i <= n; i += t )vis[b[a[i]]]++;
    for( int i = t; i <= n; i += t )ret += vis[a[b[i]]];
    for( int i = t; i <= n; i += t )vis[b[a[i]]]--;
    return ret;
}
int main()
{
    Mobius();
    while(~scanf("%d",&n)){
        for( int i = 1; i <= n; i++ ){
            scanf("%d",a+i);
        }
        for( int i = 1; i <= n; i++ ){
            scanf("%d",b+i);
        }
        LL ans = 0;
        for( int i = 1; i <= n; i++ ){
            ans += mu[i]*cal(i);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值