bzoj2818

题意:1<=x,y<=n,gcd(x,y)==质数的对数

解1:欧拉函数
gcd(x,y)==k
gcd(x/k, y/k)==1
那么题意就变成了在 [ 1, n/k ]之间有多少对( x,y )互质,然后我们用到了欧拉函数。
这里我们用到了一个线性欧拉筛顺便筛出来素数

void getphi() {
    phi[1] = 1;
    int cnt = 0;
    for (int i = 2; i <= N; i++) {
        if (!check[i]) {
            pri[++cnt] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; j <= cnt; j++) {
            if (i * pri[j] > N) break;
            check[i * pri[j]] = 1;
            if (i % pri[j] == 0) {
                phi[i * pri[j]] = phi[i] * pri[j];
                break;
            }
            else phi[i * pri[j]] = phi[i] * (pri[j] - 1);
        }
    }
    for (int i = 1; i <= N; i++)
        sum[i] = (sum[i - 1] + phi[i]) % mod;
}

将欧拉函数处理成前缀和,再枚举小于n的质数p将sum[n/p]加和
因为(x,y)(y,x)我们都要算,但是(1,1)这个算了两遍所以 -1
ans=∑(sum [ n/ pri [ i ] ])*2-1 (pri[ i ]

完整代码如下

#include
using namespace std;
typedef long long ll;
const int N=1e7+5;

int n,tot;
int phi[N],pri[N];
bool mark[N];
ll sum[N];

void getphi()//线性筛欧拉顺便筛质数
{
    phi[1]=1;
    tot=0;
    memset(mark,0,sizeof(mark));
    mark[0]=1;
    mark[1]=1;
    for(int i=2; i<=N; i++)
    {
        if(!mark[i])
        {
            phi[i]=i-1;
            pri[++tot]=i;
        }
        for(int j=1; j<=tot&&i*pri[j]<=N; j++)
        {
            int x=pri[j];
            mark[i*x]=1;
            if(i%x==0)
            {
                phi[i*x]=phi[i]*x;
                break;
            }
            else
                phi[i*x]=phi[i]*(pri[j]-1);
        }
    }
}
int main()
{

    scanf("%d",&n);
    getphi();
    for(int i=1; i<=n; i++)
    {
        sum[i]=sum[i-1]+phi[i];
    }
    ll ans=0;
    for(int i=1; i<=tot; i++)
    {
        ans+=sum[n/pri[i]]*2-1;
    }
    printf("%lld\n",ans);
    return 0;
}

解2:莫比乌斯反演
我们设f(n)为gcd(i,j)=k的个数,F(n)为gcd(i,j)=k的倍数的个数,k是质数
同样gcd(x,y)=k,gcd(x/k, y/k)=1,我们的范围变成了【1,n/k】
我们要求的就是f(1),代入莫比乌斯反演倍数公式
f(n)=∑n|dμ(d/n)F(d) n=1
f(1)=∑μ(d)F(d) (∑的范围从d=1到d=n/k)

上面的式子两个乘数要向下取整我打不出来惹QAQ

完整代码如下:

#include 
#include 
using namespace std;
typedef long long ll;
const int N=1e7+10;
int mo[N],prime[N],n,cnt;
bool vis[N];
void mobius()
{
    mo[1]=1;
    vis[1]=1;
    for(int i=2;i

这个比较迷用莫比乌斯一定要先预处理再输入,,,

你可能感兴趣的:(题解,数论)