题目链接;BZOJ 2154 Crash的数字表格
题意:
∑i=1i=n∑j=1j=mlcm(i,j) % 100000009 (n,m≤107)
分析:
ans=∑d=1d=n∑i=1i=n∑j=1j=mi∗jd (gcd(i,j)=d)
=∑d=1d=n]∑i=1nd∑j=1mdi∗j∗d2d(gcd(i,j)=1)
=∑d=1nd∑i=1nd∑j=1md(i∗j)(gcd(i,j)=1)
定义f(d)=∑ai=1∑bj=1(i∗j) (gcd(i,j)=d)
定义F(d)=∑i=ai=1∑j=bj=1(i∗j) (gcd(i,j) % d=0),
易得F(1)=sum(a,b)=a(a+1)2∗b(b+1)2
反演可得:f(1)=∑i=ai=1∑j=bj=1(i∗j)(gcd(i,j)=1,a≤b)
=∑ax=1μ(x)∗x2∑axi=1∑bxj=1(i∗j)
=∑ax=1μ(x)∗x2∑axi=1i∗∑bxj=1j
=∑ax=1μ(x)∗x2∗sum(ax,bx)
所以ans=∑nd=1d∑n′x=1μ(x)∗x2∗sum(n′x,m′x)(n′=nd,m′=md)
此时如果预处理出 μ(x)∗x2 的前缀和求 ans 的复杂度是 O(n−−√∗n−−√)=O(n) ,对于本题而言是不够的。令 T=d∗x 可得: ans=∑nT=1sum(nT,mT)∑x|TTx∗x2∗μ(x) .令 h[T]=∑x|nTx∗x2∗μ(x) ,预处理出 h[T] 【因为 h(T) 是积性函数可以线性筛】,那么时间复杂度就变为 O(n−−√) 总的时间复杂度为 O(Tn)(T是测试组数)
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <bitset>
using namespace std;
typedef long long ll;
const int MAX_N =10000010;
const ll mod = 100000009;
bitset<MAX_N> bs;
int prime_cnt, prime[MAX_N / 100 * 7];
ll h[MAX_N], sum[MAX_N];
void GetMu()
{
bs.set();
prime_cnt = 0;
h[1] = sum[1] = 1;
for(int i = 2; i < MAX_N; ++i) {
if(bs[i]) {
prime[prime_cnt++] = i;
h[i] = (ll)i * (1 - i) % mod;
}
for(int j = 0; j < prime_cnt && i * prime[j] < MAX_N; ++j ){
bs[i * prime[j]] = 0;
if(i % prime[j]) {
h[i * prime[j]] = h[i] * h[prime[j]] % mod;
}else {
h[i * prime[j]] = h[i] * prime[j] % mod;
break;
}
}
}
for(int i = 1; i < MAX_N; ++i ) {
sum[i] = ((sum[i - 1] + h[i]) % mod + mod) % mod;
}
}
inline ll work(int n, int m)
{
ll res1 = (ll) n * (n + 1) / 2 % mod;
ll res2 = (ll) m * (m + 1) / 2 % mod;
return res1 * res2 % mod;
}
inline ll solve(int n, int m)
{
int top = min(n, m), last;
ll res = 0;
for(int i = 1; i <= top; i = last + 1) {
last = min( n / (n / i), m / (m / i));
res = (res + (sum[last] - sum[i - 1] + mod) % mod * work(n / i, m / i) % mod) % mod;
}
return res;
}
int main()
{
GetMu();
int T, n, m;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
printf("%lld\n", solve(n, m));
}
return 0;
}