P2260 [清华集训2012]模积和

P2260 [清华集训2012]模积和

整除分块+逆元

详细题解移步P2260题解板块

式子可以拆开分别求解,具体见题解

这里主要讲的是整除分块(数论分块)mod不为素数时如何求逆元

整除分块:求Σ「n/i」(i=1~n),「」表示向下取整

由于「n/i」在某段区间内都有相同的值,所以可以分块算,复杂度O( sqrt(n) )

code:

ll res=0;
for(ll l=1,r;l<=n;l=r+1){
  r=n/(n/l);
  res=res+(r-l+1)*(n/l);
}
return
res;

当mod是素数时,我们可以用费马小定理直接算,但是mod不为素数时,我们就可以用欧拉函数算(定义等右转Baidu)

当n,p互素时,n在 mod p 下的乘法逆元为 n^(phi(p)-1)

当n,p不互素时,并没有乘法逆元233333

特别地,当p为素数时,phi(p)=p-1,恰好是费马小定理

本人能力限制,不给出证明(逃

当所求逆元的数事先指定时,可以用暴力法(比如本题只需求2,6的逆元)

下面给出暴力法和欧拉函数的code:

#include<bits/stdc++.h>
using namespace std;
const int mod=19940417;
template <typename T> int find_inv(T x){ //暴力预求
    for(int i=1;i<=71806291;++i) //自行规定范围
        if(1LL*x*i%mod==1) //根据定义
            return i;
}
template <typename T> int _phi(T x){ //欧拉函数计算
    int k=sqrt(x),phi=x;
    for(int i=2;i<=k;++i)
        if(x%i==0){
            phi=phi/i*(i-1);
            while(x%i==0) x/=i;
        }
    if(x>1) phi=phi/x*(x-1);
    return phi;
}
int ksm(int x,int y){
    int res=1;
    for(;y;y>>=1){
        if(y&1) res=1LL*res*x%mod;
        x=1LL*x*x%mod;
    }return res;
}
int main(){
    cout<<find_inv(2)<<" "<<find_inv(6)<<endl;
    int phi=_phi(mod);
    cout<<ksm(2,phi-1)<<" "<<ksm(6,phi-1);
    //2->9970209 6->3323403
    return 0;
}

接下来就是本题的code了

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
template <typename T> inline T min(T &a,T &b) {return a<b ?a:b;}
const int mod=19940417;
ll n,m,ans;
inline ll sum1(ll l,ll r) {return (l+r)*(r-l+1)%mod*9970209%mod;} //求等差数列
inline ll sum2(ll x) {return x*(x+1)%mod*(x*2+1)%mod*3323403%mod;} //求1^2+2^2+3^2+...+x^2
inline ll calc(ll x){ //整除分块(根据题意稍作修改)
    ll res=0;
    for(ll l=1,r;l<=x;l=r+1) r=x/(x/l),res=(res+(r-l+1)*x-sum1(l,r)*(x/l)+mod)%mod;
    return res;
}
int main(){
    scanf("%lld%lld",&n,&m); if(n>m) swap(n,m);
    ans=calc(n)*calc(m)%mod; ll s1,s2,s3;
    for(ll l=1,r;l<=n;l=r+1){
        r=min(n/(n/l),m/(m/l));
        s1=n*m%mod*(r-l+1)%mod;
        s2=(n/l*m+m/l*n)%mod*sum1(l,r)%mod;
        s3=(n/l)*(m/l)%mod*(sum2(r)-sum2(l-1)+mod)%mod;
        ans=(ans-(s1-s2+s3+mod)%mod+mod)%mod; //式子拆分后分别求解
    }printf("%lld",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/kafuuchino/p/9683217.html