【WinterCamp 2013】模积和

20 篇文章 0 订阅
7 篇文章 0 订阅

Description

i=1nj=1,jim(nmodi)(mmodj)

n,m<=10^9,答案模19940417

Solution

首先,看到有%,心里很不爽,把它变成 nnii ,对于 ij 的情况,后面减去就可以了。我们设 n<m
于是原式就变成了

i=1nj=1m(nnii)(mmjj)i=1n(nnii)(mmii)

再设
f(n)=i=1nnnii

于是原式就变成了
f(n)f(m)i=1n(nnii)(mmii)

考虑分成两部分计算

Part 1

f(n)=i=1nnnii

=n2i=1nnii

发现 ni 最多只有 n 种取值,于是我们可以使用分块。(分块大法好!)
l 为一段连续的ni相等的左端点,那么这个区间的右端点 r 便满足nr>=nl,并且为最大值。移项得 r=nnl 。那就可以利用高斯求和搞出来了。

Part 2

i=1n(nnii)(mmii)

=i=1nnmnimimini+nimii2

=n2m+i=1ni(nmi+mni)nimii2

这样也就可以利用分块大法打出来了。(分块大法好!)
顺便说一下,
ni=1i2=n(n+1)(2n+1)6

这里用逆元,6对于19940417的逆元是3323403。(请叫我雷锋(>_<))

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
#define mo 19940417
#define ni 3323403
using namespace std;
ll n,m,ans;
ll sum(ll l,ll r) {return (l+r)*(r-l+1)/2%mo;}
ll mi(ll n) {
    return n*(n+1)%mo*(2*n+1)%mo*ni%mo;
}
ll getan(ll n) {
    ll l=1,r;ll an=n*n%mo;
    while (l<=n) {
        r=(n/(n/l));
        an=(an-(n/l)*sum(l,r)%mo+mo)%mo;
        l=r+1;
    }
    return an;
}
int main() {
    scanf("%lld%lld",&n,&m);
    if (n>m) swap(n,m);
    ans=(getan(n)*getan(m)%mo-n*n%mo*m%mo+mo)%mo;
    ll l=1,r;
    while (l<=n) {
        r=min(n/(n/l),m / (m / l));ll x=n / l,y=m / l;
        ans=(ans+sum(l,r)*(n*y%mo+m*x%mo)%
        mo-(mi(r)-mi(l-1)+mo)%mo*x%mo*y%mo+mo)%mo;
        l=r+1;
    }
    printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值