题目描述
求 ∑∑((nmodi)∗(mmodj))其中1<=i<=n,1<=j<=m,i≠j ∑ ∑ ( ( n m o d i ) ∗ ( m m o d j ) ) 其 中 1 <= i <= n , 1 <= j <= m , i ≠ j 。
例如对于 n=3,m=4 :
答案为(3 mod 1)(4 mod 2)+(3 mod 1) (4 mod 3)+(3 mod 1) * (4 mod 4) + (3 mod 2) * (4 mod 1) + (3 mod 2) * (4 mod 3) + (3 mod 2) * (4 mod 4) + (3 mod 3) * (4 mod 1) + (3 mod 3) * (4 mod 2) + (3 mod 3) * (4 mod 4) = 1
题解
一开始看漏了
i≠j
i
≠
j
,以为这和余数求和一样…
但是既然又和取模的和有关,与余数求和的思想是差不多的。
我们有
amodb=a−floor(a/b)∗b
a
m
o
d
b
=
a
−
f
l
o
o
r
(
a
/
b
)
∗
b
于是我们再来推一波式子:
=∑∑((nmodi)∗(mmodj))−∑min(n,m)i=1((nmodi)∗(mmodi)) = ∑ ∑ ( ( n m o d i ) ∗ ( m m o d j ) ) − ∑ i = 1 m i n ( n , m ) ( ( n m o d i ) ∗ ( m m o d i ) )
前面那段就是余数求和的方法,不多讲,我们来看后面一段。
不妨设
n≤m
n
≤
m
那么:
∑ni=1((nmodi)∗(mmodi))
∑
i
=
1
n
(
(
n
m
o
d
i
)
∗
(
m
m
o
d
i
)
)
=∑((n−n/i∗i)∗(m−m/i∗i))
=
∑
(
(
n
−
n
/
i
∗
i
)
∗
(
m
−
m
/
i
∗
i
)
)
令
n/i=x,m/i=y
n
/
i
=
x
,
m
/
i
=
y
原式化为:
∑(mn−mxi−nyi+xy∗i2)
∑
(
m
n
−
m
x
i
−
n
y
i
+
x
y
∗
i
2
)
∑(mn+xy∗i2−(mx+ny)∗i)
∑
(
m
n
+
x
y
∗
i
2
−
(
m
x
+
n
y
)
∗
i
)
到这里式子已经推的差不多了。
我们发现可以对
x,y
x
,
y
进行除法分块,且前面的mn可以预先处理掉。
于是我们着眼于求出一个块内的
(xy∗i2−(mx+ny)∗i)
(
x
y
∗
i
2
−
(
m
x
+
n
y
)
∗
i
)
假定当前块内
xy=S1,mx+ny=S2
x
y
=
S
1
,
m
x
+
n
y
=
S
2
那么要求的就是:
S1∗i2−S2∗i
S
1
∗
i
2
−
S
2
∗
i
前面这一坨要用到平方和公式即:
∑ni=1(i2)=n∗(n+1)∗(2n+1)/6
∑
i
=
1
n
(
i
2
)
=
n
∗
(
n
+
1
)
∗
(
2
n
+
1
)
/
6
注意这里要求个逆元。
后面的直接等差数列求和即可,那么本题就做完了。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
typedef long long ll;
const int N=1e9+10;
const ll mod=19940417;
const ll inv=3323403;//6的逆元
inline ll Mod_sum(ll k,ll n)
{
ll ans=n*k;
register ll l=1,r=0;
for(l=1;l<=n;l=r+1)
{
if(k/l!=0) r=min(k/(k/l),n);
else r=n;
ans-=((k/l)*(r-l+1)*(l+r)/2)%mod;
if(ans<0) ans+=mod;
}
return ans%mod;
}
inline ll sum(ll l,ll r)
{
return ((r+l)*(r-l+1)/2)%mod;
}
inline ll sum2(ll n)
{
if(n==0) return 0;
if(n==1) return 1;
return ((n*(n+1)%mod)*(2*n%mod+1)%mod)*inv%mod;
}
int main()
{
register ll ans=0;ll n,m;
scanf("%lld %lld",&n,&m);
ans=Mod_sum(n,n);
ans*=Mod_sum(m,m);
if(n>m) swap(n,m);
ans-=(n*(m*n%mod))%mod;
ans=(ans+mod)%mod;
ll l;ll r=0;
for(l=1;l<=n;l=r+1){
r=min(m/(m/l),n/(n/l));//取两个都相同的块
register ll s1=((m/l)*(n/l)%mod)*((sum2(r)-sum2(l-1)+mod)%mod)%mod;//平方和公式
register ll s2=(((n*(m/l)%mod+m*(n/l)%mod)%mod)*sum(l,r))%mod;
ans=(ans-(s1-s2+mod)+mod)%mod;
}
while(ans<0) ans+=mod;
printf("%lld\n",ans);
return 0;
}