【bzoj2956】模积和 数论
题目描述
求∑∑((n mod i)*(m mod j))其中1<=i<=n,1<=j<=m,i≠j。
输入
第一行两个数n,m。
输出
一个整数表示答案mod 19940417的值
样例输入
3 4
样例输出
1
题解
数论+分块
由于直接求i≠j的情况比较难搞,所以我们可以先求出i可以等于j的和,然后再减去i等于j时的情况。
也就是求∑∑((n mod i)*(m mod j))-∑((n mod i)*(m mod i))。
然后再根据乘法分配律转化为∑(n mod i)*∑(m mod i)-∑((n mod i)*(m mod i))。
因为有n mod i = n-(n/i)*i(这里的除号均表示向下取整)。
所以∑(n mod i) = ∑(n-(n/i)*i) = n*n-∑((n/i)*i)。
这里n/i最多只有√n 种取值,我们可以分块来求,这里用到1,2,3,...,n的和。
后面一坨变为∑((n-(n/i)*i)*(m-(m/i)*i))=∑(nm-m*(n/i)*i-n/(m/i)*i+(n/i)*(m/i)*i^2)。
同样的方法处理,运用一下1^2,2^2,3^2,...,n^2的和。
最后减一下即可。
然而有一个问题,就是套用公式的时候需要除法,不能先取模,而不先取模还会爆long long。
好在除的数是固定的2和6,于是可以直接把mod开大6倍,最后再模回去即可。
#include <cstdio> #include <algorithm> #define MOD 119642502 using namespace std; typedef long long ll; ll sum1(ll p) { return p * (p + 1) % MOD / 2; } ll sum2(ll p) { return p * (p + 1) % MOD * (2 * p + 1) % MOD / 6; } ll calc1(ll n) { ll ans = n * n % MOD , i , last = 0; for(i = 1 ; i <= n ; i = last + 1) { last = n / (n / i); ans = (ans - (n / i) % MOD * (sum1(last) - sum1(i - 1) + MOD) % MOD + MOD) % MOD; } return ans; } ll calc2(ll n , ll m) { ll ans = n * m % MOD * min(n , m) % MOD , i , last = 0; for(i = 1 ; i <= n && i <= m ; i = last + 1) { last = min(n / (n / i) , m / (m / i)); ans = (ans - m * (n / i) % MOD * (sum1(last) - sum1(i - 1) + MOD) % MOD - n * (m / i) % MOD * (sum1(last) - sum1(i - 1) + MOD) % MOD + (n / i) * (m / i) % MOD * (sum2(last) - sum2(i - 1) + MOD) % MOD + 2 * MOD) % MOD; } return ans; } int main() { ll n , m; scanf("%lld%lld" , &n , &m); printf("%lld\n" , (calc1(n) * calc1(m) % MOD - calc2(n , m) + MOD) % (MOD / 6)); return 0; }