[联合集训6-26] 盒子 莫比乌斯反演+杜教筛

题目就是求 i = 1 n j = 1 n i + j gcd ( i , j ) 2 n 2
枚举 d = gcd ( i , j )

d = 1 n i = 1 n j = 1 n [ gcd ( i , j ) = d ] i + j d

莫比乌斯反演,
k = 1 n μ ( k ) d = 1 n k i = 1 n k d i = 1 n k d i k d + j k d d = k = 1 n μ ( k ) k d = 1 n k n k d 2 ( n k d + 1 )

f ( n ) = i = 1 n μ ( k ) k , g ( n ) = i = 1 n n i 2 ( n i + 1 ) 。我们只要能筛 f , g 的就能分块求了。 g 显然能直接分块 O ( n ) 求, f 可以卷上 i d 函数之后用杜教筛求,具体地:
f ( n ) = i = 1 n d | i μ ( d ) d i d i = 2 n d | i , d < i μ ( d ) d i d

f ( n ) = 1 i = 2 n i f ( n i )

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define up(x,y) (x=(x+(y))%mod)
using namespace std;
const int mod=1000000007,R=10000000;
int n,mk[R+5],pri[670000],num;
bool flag[R+5];
ll ans;
struct ha
{
    int cnt,con[R+5],a[R+5],b[R+5],nxt[R+5];
    void add(int x,int y)
    {
        int id=x%R;
        a[++cnt]=x;b[cnt]=y;
        nxt[cnt]=con[id];
        con[id]=cnt;
    }
    int qry(int x)
    {
        int id=x%R;
        for(int p=con[id];p;p=nxt[p])
            if(a[p]==x) return b[p];
        return -1;  
    }
}H;
void getpri(int n)
{
    memset(flag,1,sizeof(flag));
    flag[1]=0;mk[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(flag[i]) {pri[++num]=i;mk[i]=-i;}
        for(int j=1;j<=num&&i*pri[j]<=n;j++)
        {
            flag[i*pri[j]]=0;
            if(i%pri[j]==0) {mk[i*pri[j]]=0;break;}
            mk[i*pri[j]]=-mk[i]*pri[j];
        }
    }
    for(int i=2;i<=n;i++)
        up(mk[i],mk[i-1]);
}
ll djs(int n)
{
    if(n<=R) return mk[n];
    int hh=H.qry(n);
    if(hh!=-1) return hh;
    ll re=1;
    for(int l=2,r,x;l<=n;l=r+1)
        x=n/l,r=n/x,up(re,-djs(x)*((ll)(l+r)*(r-l+1)/2)%mod);
    H.add(n,re);
    return re; 
}
ll calc(int n)
{
    ll re=0;
    for(int l=1,r,x;l<=n;l=r+1)
        x=n/l,r=n/x,up(re,(ll)x*x%mod*(x+1)%mod*(r-l+1));
    return re;
}
ll solve(int n)
{
    ll re=0;
    for(int l=1,r,x;l<=n;l=r+1)
        x=n/l,r=n/x,up(re,(djs(r)-djs(l-1))*calc(x));
    return re;  
}
int main()
{
    freopen("box.in","r",stdin);
    freopen("box.out","w",stdout);
    scanf("%d",&n);
    getpri(R);  
    printf("%lld",((solve(n)-2ll*n*n)%mod+mod)%mod);        
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dofypxy/article/details/80820488