BJ 集训测试6 求和

这里写图片描述
n<=1e9 给定一个模数
现场有13人AC..我..好菜啊
对原式化简
d=1ndi=1nj=1ik=1i[gcd(i,j,k)==d] ∑ d = 1 n d ∑ i = 1 n ∑ j = 1 i ∑ k = 1 i [ g c d ( i , j , k ) == d ]
d=1ndi=1ndj=1ik=1i[gcd(i,j,k)==1] ∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 i ∑ k = 1 i [ g c d ( i , j , k ) == 1 ]
d=1ndi=1ndj=1ik=1iε(gcd(i,j,k)) ∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 i ∑ k = 1 i ε ( g c d ( i , j , k ) )
d=1ndi=1ndj=1ik=1id|gcd(i,j,k)μ(d) ∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 i ∑ k = 1 i ∑ d ′ | g c d ( i , j , k ) μ ( d ′ )
d=1ndd=1ndμ(d)i=1nddj=1ik=1i1 ∑ d = 1 n d ∑ d ′ = 1 ⌊ n d ⌋ μ ( d ′ ) ∑ i = 1 ⌊ n d d ′ ⌋ ∑ j = 1 i ∑ k = 1 i 1
设D为 dd d ∗ d ′ 观察到后面为 i=1nddi2 ∑ i = 1 ⌊ n d d ′ ⌋ i 2
将前半部分替换卷积 后半部分替换公式
D=1nd|DDdμ(d)ndd(ndd+1)(2ndd)6 ∑ D = 1 n ∑ d ′ | D D d ′ μ ( d ′ ) ⌊ n d d ′ ⌋ ∗ ( ⌊ n d d ′ ⌋ + 1 ) ∗ ( 2 ∗ ⌊ n d d ′ ⌋ ) 6
D=1nφ(D)ndd(ndd+1)(2ndd)6 ∑ D = 1 n φ ( D ) ⌊ n d d ′ ⌋ ∗ ( ⌊ n d d ′ ⌋ + 1 ) ∗ ( 2 ∗ ⌊ n d d ′ ⌋ ) 6
因为后半部分可以考虑到分块计算所以只需要前半部分前缀和即可 那么杜教筛 预处理欧拉函数的前 2/3使得复杂度降为 n23 n 2 3
顺带学习了发杜教筛 下面乘号部分代表卷积符号
考虑两个积性函数的卷积 (fg)(n)=h ( f ∗ g ) ( n ) = h 如何求积性函数前缀和
考虑 φ1=id φ ∗ 1 = i d
顾前缀和可以表示为 i=1ni=i=1nd|i1(d)φ(nd) ∑ i = 1 n i = ∑ i = 1 n ∑ d | i 1 ( d ) ∗ φ ( n d ) 常见套路
i=1ni=d=1ni=1ndφ(i) ∑ i = 1 n i = ∑ d = 1 n ∑ i = 1 ⌊ n d ⌋ φ ( i )
H[i] H [ i ] 表示 φ φ 的前缀和
那么显然 n(n+1)2=d=1nH(nd) n ∗ ( n + 1 ) 2 = ∑ d = 1 n H ( n d )
n(n+1)2d=2nH(nd)=H(n) n ∗ ( n + 1 ) 2 − ∑ d = 2 n H ( n d ) = H ( n ) 好了剩下部分可以递归搞了

#include<cstdio>
#include<algorithm>
#define ll long long
#define N 1100000
using namespace std;
int prime[N/10],tot,mod,inv6,n;
bool not_prime[N];ll s[N],ans,phi[N];
inline int ksm(int x,int t){
    int tmp=1;for (;t;x=(ll)x*x%mod,t>>=1) if (t&1) tmp=(ll)tmp*x%mod;return tmp;
}
inline ll calc_phi(ll x){
    if (x<=1e6) return phi[x];if (s[n/x]) return s[n/x];
    ll tmp=x*x+x>>1;int last=1;
    for (int i=2;i<=x;i=last+1){
        last=x/(x/i);tmp-=calc_phi(x/i)*(last-i+1);
    }return s[n/x]=tmp;
}
inline int calc(int x){
    return (ll)x*(x+1)%mod*(x<<1|1)%mod*inv6%mod;
}
int main(){
    freopen("sum.in","r",stdin);
    for (int i=2;i<=1e6;++i){
        if (!not_prime[i]) prime[++tot]=i,phi[i]=i-1;   
        for (int j=1;prime[j]*i<=1e6;++j){
            not_prime[i*prime[j]]=1;
            if (i%prime[j]==0){
                phi[i*prime[j]]=prime[j]*phi[i];break;
            }else  phi[i*prime[j]]=phi[i]*phi[prime[j]];
        }
    }scanf("%d%d",&n,&mod);int last=1;inv6=ksm(6,mod-2);
    //for (int i=1;i<=10;++i) printf("%d ",phi[i]);puts("");
    phi[1]=1;for (int i=2;i<=1e6;++i) phi[i]+=phi[i-1];
    for (int i=1;i<=n;i=last+1){
        last=n/(n/i);ll ans1=(calc_phi(last)-calc_phi(i-1))%mod,ans2=calc(n/i);
        ans+=ans1*ans2%mod;ans%=mod;
    //  printf("%lld %lld %lld %d\n",ans,ans1,ans2,i);
    }printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值