51nod 1675 序列变换 莫比乌斯反演

来源:互联网 发布:js 创建class 编辑:程序博客网 时间:2024/06/08 02:50

题意

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外都满足条件。
n<=100000,1<=a[i],b[i]<=n

分析

一开始没什么思路,后来看到评论有人说用反演可做,于是就会了。
按照反演套路,我们设f(d)表示gcd(x,y)=d其余条件不变的方案数,g(d)表示d|gcd(x,y)其余条件不变的方案数。显然我们要求的就是f(1)。如果求出g(d),那么就可以O(n)求出f(1)。而根据g(d)的定义,我们可以得到我们可以选的x或y必然满足d|x,那么就可以直接拿桶来统计了。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int N=100005;int n,a[N],b[N],t[N],tot,prime[N],mu[N];LL f[N],g[N];bool not_prime[N];void get_prime(int n){    mu[1]=1;    for (int i=2;i<=n;i++)    {        if (!not_prime[i]) prime[++tot]=i,mu[i]=-1;        for (int j=1;j<=tot&&i*prime[j]<=n;j++)        {            not_prime[i*prime[j]]=1;            if (i%prime[j]==0) break;            mu[i*prime[j]]=-mu[i];        }    }}int main(){    scanf("%d",&n);    get_prime(n);    for (int i=1;i<=n;i++) scanf("%d",&a[i]);    for (int i=1;i<=n;i++) scanf("%d",&b[i]);    for (int d=1;d<=n;d++)    {        for (int i=d;i<=n;i+=d) t[b[a[i]]]++;        for (int i=d;i<=n;i+=d) g[d]+=t[a[b[i]]];        for (int i=d;i<=n;i+=d) t[b[a[i]]]=0;    }    LL ans=0;    for (int i=1;i<=n;i++) ans+=(LL)g[i]*mu[i];    printf("%lld",ans);    return 0;}
阅读全文
0 0
原创粉丝点击