【BZOJ 2671】Calc

题目要求求出 Na=1Nb=a+1[a+b|ab]
我们设 d=gcd(a,b),a=nd,b=md 则转化为
[nd+md|nmd2]=[n+m|nmd] 又有 n,m 互质,则显然有 n+m|d
原式可推为
Nd=1Ndn=1Ndm=n+1e(gcd(n,m))[n+m|d]
[n+m|d]的个数显然为 Nm(n+m)
e(gcd(n,m)) 显然可以莫比乌斯反演
所以进一步推为
=Nm=1m1n=1Nm(n+m)d|n,d|mμ(d)
md,nd 来替代 m,n 则有
Nd=1μ(d)Ndm=1m1n=1Nd2mn+m
于是可以分块求得,code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
long long n,sqn,tot;
long long mu[100001],prime[10001];
bool a[100001];
void prework(long long n)
{
    long long i,j;
    mu[1]=1;
    for (i=2;i<=n;++i)
      {
        if (!a[i])
          {mu[i]=-1; prime[++tot]=i;}
        for (j=1;j<=tot&&i*prime[j]<=n;++j)
          {
            a[i*prime[j]]=true;
            if (i%prime[j]==0)
              {
                mu[i*prime[j]]=0;
                break;
              }
            mu[i*prime[j]]=-mu[i];
          }
      }
}
long long work(long long fz,long long n)
{
    long long ans=0,i,last;
    if (n==0) return 0;
    for (i=n+2;i<=2*n+1;i=last+1)
      {
        if (fz/i==0) break;
        last=min(2*n+1,fz/(fz/i));
        ans=ans+(last-i+1)*(fz/i);
      }
    return ans;
}
int main()
{
    long long d,m,ans=0,t;
    scanf("%lld",&n);
    sqn=sqrt(n);
    prework(sqn);
    for (d=1;d<=sqn;++d)
      {
        if (mu[d]==0) continue;
        t=0;
        for (m=2;m<=sqn/d;++m)
          t+=work(n/(d*d*m),m-1);
        ans=ans+mu[d]*t;
      }
    printf("%lld\n",ans);
}

你可能感兴趣的:(【BZOJ 2671】Calc)