BZOJ 2693: jzptab(莫比乌斯反演+线性筛)

题目大意

ni=1mj=1[i,j] ,答案模 108+9 多组询问。

T <= 10000
N, M<=10000000


思路

Crash的数字表格强化版。

我们进一步优化柿子

ans=d=1ndt=1ndt2μ(t)Sum(ndt,mdt)

枚举 dt

ans=D=1nSum(nD,mD)Di|Diμ(i)

G(D)=Di|Diμ(i) ,如果我们能求出 G(D) 的前缀和,那么就可以用 O(n) 的时间处理每一个询问。

注意到积性函数的约数和也是积性函数。(为啥我没注意到呢)

于是 G(D) 也是积性函数,我们线性筛可求之。

对于 i%prime[j]==0 的情况,我们发现相对于 i 新增的约数 x 必然有不止一个 prime[j] μ(x)=0 ,所以这些约数对答案没有贡献,对 i|Diμ(i) 有贡献的 i 还是 D 的约数,新增的 prime[j] 只对 D=Dprime[j] 有贡献罢了。

于是我们可以预处理出 G(D) 的前缀和了。本题在 O(Tn) 的时间内即可解决。

PS:本题的模数真是鬼畜!若不是我无聊去数了一下,又要调一年!


代码

#include 
#define maxn 10000010
#define MOD 100000009
#define temp (i * prime[j])

using namespace std;

int n, m, T, cnt;
int prime[maxn], miu[maxn], g[maxn];
bool Vis[maxn];

void Da(){
    miu[1] = g[1] = 1;
    for(int i = 2; i < maxn; i++){
        if(!Vis[i]){
            prime[++cnt] = i;
            miu[i] = -1;
            g[i] = (i - 1LL * i * i % MOD + MOD) % MOD;
        }
        for(int j = 1; j <= cnt && temp < maxn; j++){
            Vis[temp] = true;
            if(i % prime[j] == 0){
                miu[temp] = 0;
                g[temp] = 1LL * g[i] * prime[j] % MOD;
                break;
            }
            else{
                miu[temp] = -miu[i];
                g[temp] = 1LL * g[i] * g[prime[j]] % MOD;
            }
        }
    }
    for(int i = 2; i < maxn; i++)  g[i] = (g[i] + g[i-1]) % MOD;
}


int Sum(int x, int y){
    int temp1 = (1LL * x * (x + 1) >> 1) % MOD;
    int temp2 = (1LL * y * (y + 1) >> 1) % MOD;
    return 1LL * temp1 * temp2 % MOD;
}

int Solve(){
    if(n > m)  swap(n, m);
    int ans = 0, last;
    for(int i = 1; i <= n; i = last+1){
        last = min(n/(n/i), m/(m/i));
        ans = (ans + 1LL * (g[last] - g[i-1] + MOD) % MOD * Sum(n/i, m/i)) % MOD;
    }
    return ans;
}

int main(){

    scanf("%d", &T);

    Da();

    while(T --){
        scanf("%d%d", &n, &m);

        printf("%d\n", Solve());
    }

    return 0;
} 

你可能感兴趣的:(数论,&,数学,莫比乌斯反演,&,杜教筛,BZOJ)