2018SD省队集训R2 D5

本文详细解析了一道关于数论的题目,通过数学变换和优化技巧,将原始问题转化为易于计算的形式。从暴力筛选到莫比乌斯反演,逐步优化算法,最终实现了高效的求解方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

T2

这里写图片描述

题解

前排鸣谢LCR小姐姐QAQ

首先暴力欧拉筛模拟这个过程就是27pts的

70pts就是化柿子+卡常数

我们可以发现f(n)=(px11+1)(px22+1)...(pxkk+1)f(n)=(p1x1+1)(p2x2+1)...(pkxk+1)

那么他的形式就大概是f(a1)f(a2)...f(ak)f(a1)f(a2)...f(ak)类似这个样子,然后我们可以枚举f(ij)=f(i)f(j)f(ij)=f(i)f(j),只要ij<=n那么就是我们要求的答案的一部分,即f(ij),容斥一下答案也就是ab<=n[(a,b)=1]a∑ab<=n[(a,b)=1]a

化个柿子

i=1nij=1ni[(i,j)=1]∑i=1ni∑j=1⌊ni⌋[(i,j)=1]

然后反演
=i=1nij=1nid|(i,j)μ(d)=∑i=1ni∑j=1⌊ni⌋∑d|(i,j)μ(d)

套路的枚举d
=d=1nμ(d)d|iinid=∑d=1nμ(d)∑d|ii⌊⌊ni⌋d⌋

i可以枚举d的倍数即i=id
=d=1nμ(d)i=1ndidnidd=∑d=1nμ(d)∑i=1⌊nd⌋id⌊⌊nid⌋d⌋

=d=1nμ(d)i=1ndidnd2i=∑d=1nμ(d)∑i=1⌊nd⌋id⌊⌊nd2⌋i⌋

我们可以发现当d^2>n的时候这个值是0了,没有意义,而且当i>n/d^2的时候后面的柿子也是0,那么
=d=1nμ(d)i=1nd2idnd2i=∑d=1nμ(d)∑i=1⌊nd2⌋id⌊⌊nd2⌋i⌋

这样加上分块优化可以获得60+的好成绩QAQ(其实剩下的是被卡常了

然后100pts的话
前排鸣谢zyb dalao

ab<=na[(a,b)==1]∑ab<=na[(a,b)==1]

然后我们可以强制a < b,这样只要在后面加的时候加上a,b就好了
a=1nb=a+1na(a+b)[(a,b)==1]∑a=1n∑b=a+1na(a+b)[(a,b)==1]

然后莫比乌斯反演

a=1nb=a+1na(a+b)d|(a,b)μ(d)∑a=1n∑b=a+1na(a+b)∑d|(a,b)μ(d)

d=1ndμ(d)a=1ndb=a+1nad2a+b∑d=1ndμ(d)∑a=1nd∑b=a+1nad2a+b

(当然d是从后面的a+b提出来的,因为后面的a,b枚举的都是d的倍数了)

最后一个Σ是O(1)的,前面的是标准的O(nlogn)O(nlogn)卡完常数之后可以通过此题

卡常技巧:在%mod的时候,可以开一个unsigned longlong,每8次取一次%,这样似乎会快很多QAQ

代码

#include <cstdio>
#include <iostream>
#include <cmath>
#define LL long long 
#define uli unsigned long long
using namespace std;
const int N=10000005;
const LL INF=8e18;
int mod,mu[N],pri[N],tot;bool ss[N];LL n,up;
void pre()
{
    mu[1]=1;
    for (int i=2;i<=up;i++)
    {
        if (!ss[i]) pri[++tot]=i,mu[i]=-1;
        for (int j=1;j<=tot && pri[j]*i<=up;j++)
        {
            ss[pri[j]*i]=1;
            if (i%pri[j]==0) break;
            mu[pri[j]*i]=-mu[i];
        }
    }
}
LL ksm(LL a,LL k)
{
    LL ans=1;
    for (;k;k>>=1,a=a*a%mod)
      if (k&1) ans=ans*a%mod;
    return ans;
}
int main()
{
    freopen("sum.in","r",stdin);
    freopen("sum.out","w",stdout);
    scanf("%lld%d",&n,&mod);
    up=sqrt(n);pre();LL ans=0;
    for (LL d=1;d<=up;d++)
      if (mu[d])
      {
        LL ok=0;LL sb=up/d,lx=n/d/d;
        for (LL a=1;a<=sb;a++)
        {
            LL t=lx/a%mod;
            ok+=(uli)(t-a)*(t+a*3+1);
            if (ok>INF) ok%=mod;
        }
        ans+=ok%mod*d*mu[d]%mod;
      }
    ans=ans%mod*ksm(2,mod-2)%mod;
    ans++;
    printf("%lld",(ans+mod)%mod);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值