BZOJ 2956 模积和 (分块加速)

i=1n1<=j<=m,ji(n mod i)(m mod j)=i=1n1<=j<=m,ji(nini)(mjmj)=i=1n(nini)1<=j<=m,ji(mjmj)=i=1n(nini)j=1m(mjmj)i=1min(n,m)(nini)(mjmj)=(n2i=1nini)(m2i=1mimi)min(n,m)nm+(mi=1min(n,m)ini+ni=1min(n,m)imi)i=1min(n,m)i2nimi

然后分块加速搞就可以了,总的复杂度: o(n) ,注意乘积越界的问题,还有取模的问题就搞定了。

#include <bits/stdc++.h>
#define LL long long
#define FOR(i,x,y)  for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i > y;-- i)

using namespace std;

const int Mod = 19940417;
LL inv;
LL n,m;

void Gcd(LL a,LL b,LL& d,LL& x,LL& y){
    if(!b)  {d = a;x = 1;y = 0;}
    else{Gcd(b,a%b,d,y,x);y -= x*(a/b);}
}

LL Inv(LL a,LL n){
    LL d,x,y;
    Gcd(a,n,d,x,y);
    return d == 1 ? (x+n)%n : -1;
}

void work(){
    LL t1 = 0;
    for(LL i = 1;i <= m;){
        LL d = m/i;
        LL j = m/d;
        LL tem = ((i+j)*(j-i+1)/2)%Mod;
        t1 = (t1+d*tem%Mod)%Mod;
        i = j+1;
    }
    t1 = ((m*m)%Mod+Mod-t1)%Mod;
    LL t2 = 0;
    for(LL i = 1;i <= n;){
        LL d = n/i;
        LL j = n/d;
        LL tem = ((i+j)*(j-i+1)/2)%Mod;
        t2 = (t2+d*tem%Mod)%Mod;
        i = j+1;
    }
    t2 = ((n*n)%Mod+Mod-t2)%Mod;
    LL ans = t1*t2%Mod;
    int c = min(n,m);
    t1 = t2 = 0;
    LL t3 = 0;
    for(LL i = 1;i <= c;){
        LL d1 = n/i,d2 = m/i;
        LL j = min(n/d1,m/d2);
        LL tem = ((i+j)*(j-i+1)/2)%Mod;
        t1 = (t1+d1*tem%Mod)%Mod;
        t2 = (t2+d2*tem%Mod)%Mod;
        LL t_1 = (((j*(j+1)%Mod)*((j*2+1)%Mod)%Mod)*inv)%Mod;
        LL t_2 = (((i*(i-1)%Mod)*((i*2-1)%Mod)%Mod)*inv)%Mod;
        LL t_ = (t_1-t_2+Mod)%Mod;
        t_ = ((d1*d2)%Mod)*t_%Mod;
        t3 = (t3+t_)%Mod;
        i = j+1;
    }
    LL res = ((((m*n)%Mod)*c%Mod-(m*t1)%Mod-(n*t2)%Mod+t3)%Mod+Mod)%Mod;
    ans = (ans-res+Mod)%Mod;
    printf("%lld\n",ans);
}

int main()
{
    //freopen("test.in","r",stdin);
    inv = Inv(6,Mod);
    while(~scanf("%lld%lld",&n,&m)){
        work();
    }
    return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值