BZOJ 2956 模积和
Description
求$$ \sum_{i=1}^{n} \sum_{j=1}^{m} \lbrack i \neq j \rbrack(n ; mod; i)(m ; mod ; j)$$。
Input
第一行两个数\(n,m\)。
Output
一个整数表示答案\(mod\;19940417\)的值。
Sample Input
3 4
Sample Output
1
Hint
数据规模和约定
对于\(100\%\)的数据\(n,m \le 10^{9}\)。
补数论中。
这题我们要从模的本质出发:$$a ; mod ; b = a - b \times \lfloor \frac{a}{b} \rfloor$$
我们首先不考虑\(i \neq j\)条件,单独对\(i\)或\(j\)讨论,我们可以得到$$\sum_{i = 1}^{n}n;mod;i=\sum_{i = 1}^{n}n-i \times \lfloor \frac{n}{i} \rfloor$$
化简一下,可以得到$$\sum_{i = 1}^{n}n-i \times \lfloor \frac{n}{i} \rfloor = n{2}-\sum_{i=1}i \lfloor \frac{n}{i} \rfloor$$
由与\(\lfloor \frac{n}{i} \rfloor\)不同的只有\(\sqrt{n}\)个,这个式子可以\(O(\sqrt{n})\)计算。对于\(i,j\)分别做一遍相乘起来即可。
对于\(i=j\)的部分,单独拿出来减去贡献即可。我们用类似的方法,即求$$\sum_{i=1}^{min(n,m)}(n;mod;i)(m;mod;i)$$
然后跟之前一样的处理方法化为求$$\sum_{i=1}^{min(n,m)}(n-i \times \lfloor \frac{n}{i} \rfloor)(m - i \times \lfloor \frac{m}{i} \rfloor)$$
稍微化简一下就可以也\(\sqrt{n}\)分块了$$\sum_{i=1}^{min(n,m)}(nm - mi \lfloor \frac{n}{i} \rfloor - ni \lfloor \frac{m}{i} \rfloor + i^{2}\lfloor \frac{m}{i} \rfloor \lfloor \frac{n}{i} \rfloor)$$
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef long long ll;
#define rhl (19940417)
inline ll sum(ll a,ll b) { return (((ll)(a+b)*(ll)(b-a+1))>>1)%rhl; }
inline ll calc(ll n,ll m)
{
ll ret = n*m%rhl;
ll pos;
for (ll i = 1;i <= m;i = pos+1)
{
pos = min(m,n/(n/i));
(ret -= sum(i,pos)*(n/i)%rhl)%=rhl;
}
return ret;
}
inline ll pre(ll n)
{
ll a = n,b = n+1,c = 2*n+1;
return a*b%rhl*c%rhl*3323403%rhl;
}
int main()
{
freopen("2956.in","r",stdin);
freopen("2956.out","w",stdout);
ll n,m;
scanf("%lld %lld",&n,&m);
if (n > m) swap(n,m);
ll ans1 = calc(n,n),ans2 = calc(m,m);
ll ans = (ans1*ans2%rhl+rhl)%rhl;
ll pos = 1;
for (ll i = 1;i <= n;i = pos+1)
{
pos = min(n,min(n/(n/i),m/(m/i)));
(ans -= n*m%rhl*(ll)(pos-i+1))%=rhl;
(ans += sum(i,pos)*(m*(n/(ll)i)%rhl+n*(m/(ll)i)%rhl)%rhl)%=rhl;
(ans -= (pre(pos)-pre(i-1))%rhl*(n/(ll)i)%rhl*(m/(ll)i)%rhl)%=rhl;
}
printf("%lld",(ans%rhl+rhl)%rhl);
fclose(stdin); fclose(stdout);
return 0;
}