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

题目就是求 ni=1nj=1i+jgcd(i,j)2n2 ∑ i = 1 n ∑ j = 1 n i + j gcd ( i , j ) − 2 n 2
枚举 d=gcd(i,j) d = gcd ( i , j )

d=1ni=1nj=1n[gcd(i,j)=d]i+jd ∑ d = 1 n ∑ i = 1 n ∑ j = 1 n [ gcd ( i , j ) = d ] i + j d

莫比乌斯反演,
k=1nμ(k)d=1nki=1nkdi=1nkdikd+jkdd=k=1nμ(k)kd=1nknkd2(nkd+1) ∑ 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)=ni=1μ(k)k,g(n)=ni=1ni2(ni+1) f ( n ) = ∑ i = 1 n μ ( k ) ⋅ k , g ( n ) = ∑ i = 1 n ⌊ n i ⌋ 2 ( ⌊ n i ⌋ + 1 ) 。我们只要能筛 f,g f , g 的就能分块求了。 g g 显然能直接分块O(n)求, f f 可以卷上id函数之后用杜教筛求,具体地:
f(n)=i=1nd|iμ(d)didi=2nd|i,d<iμ(d)did f ( n ) = ∑ i = 1 n ∑ d | i μ ( d ) ⋅ d ⋅ i d − ∑ i = 2 n ∑ d | i , d < i μ ( d ) ⋅ d ⋅ i d

f(n)=1i=2nif(ni) 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值