题目链接 : 点这里
题意:给了一个数列a,然后给了一个区间[L, R],然后要求 sigma(gcd(i, j)), L <= i < j <= R。
解题方法: 令g(l, r) = sigma(gcd(i, j)), L <= i < j <= R.举个栗子,当a[L:R] = {4, 5, 8}时, 答案是1 + 1 + 4 = 6。由于最大公约数本身的定义,我们可以猜想多个数的gcd之和一定和这些数约数个数相关。比如4的约数1,2,4。5的约数为1,5。8的约数为1,2,4,8。=》 num[1] = 3, num[2] = 1, num[4] = 2, num[5] = 1, num[8] = 1。猜想答案为 g(l, r) = sigma(d * C(2, num(d))), 其中C(a, b) 代表组合数。这里的d代表L,R区间里面所有数的约数。但是经过栗子计算会发现答案不正确,为什么?因为显然出现了重复计数,那么我们非常显然的想到了容斥。修正一下g(l, r) = sigma(f(d) * C(2, num(d)))。其中sigma(f(d)) = n, 其中n整除d。后面这个式子是怎么来的???对于一个已经统计过的数,那么它的约数一定被统计过,为了避免重复统计,那么对n所有约数统计的次数之和等于n,f其实就是欧拉函数,学过莫比乌斯反演就非常明白这里了。。。为了解决这个问题,今天下午看了一下午PO爷的莫比乌斯反演的论文2333。所以g(l, r) = sigma(phi(d)*C(2, num(d)))。
那么直接搞显然会t的对吧,那么想我们知道[l, r]的信息可以O(1)得到[l, r+1], [l, r - 1], [l - 1, r], [l + 1, r]的信息吗???显然可以啊。其实变化就是sigma(num[d] * phi(d))。自己画一下就明白了。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e4 + 10;

int T, ks, n, m, block, a[maxn], cnt[maxn], pos[maxn];
long long ans[maxn], phi[maxn], Ans;
vector <int> factor[maxn];

struct Q{
    int l, r, id;
    Q(){}
    Q(int l, int r, int id) : l(l), r(r), id(id) {}
    bool operator < (const Q &rhs) const{
        if(pos[l] == pos[rhs.l]) return r < rhs.r;
        return pos[l] < pos[rhs.l];
    }
}q[maxn];

void pre_init(){
    for(int i = 1; i < maxn; i++)
        for(int j = i; j < maxn; j += i)
            factor[j].push_back(i);
    phi[1] = 1;
    for(int i = 2; i < maxn; i++){
        if(!phi[i]){
            for(int j = i; j < maxn; j += i){
                if(!phi[j]) phi[j] = j;
                phi[j] = phi[j] * (i - 1) / i;
            }
        }
    }
}

long long add(int x)
{
    long long res = 0;
    for(auto d : factor[x]) res += cnt[d] * phi[d];
    for(auto d : factor[x]) cnt[d]++;
    return res;
}

long long del(int x){
    long long res= 0;
    for(auto d : factor[x]) cnt[d]--;
    for(auto d : factor[x]) res += cnt[d] * phi[d];
    return -res;
}


int main(){
    pre_init();
    scanf("%d", &T); while(T--){
        scanf("%d", &n);
        block = sqrt(n);
        memset(cnt, 0, sizeof(cnt));
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]), pos[i] = (i - 1) / block;
        scanf("%d", &m);
        for(int i = 1; i <= m; i++){scanf("%d%d", &q[i].l, &q[i].r); q[i].id = i;}
        sort(q + 1, q + m + 1);
        int L = 1, R = 0;
        Ans = 0;
        for(int i = 1; i <= m; i++){
            int id = q[i].id;
            while(L < q[i].l) Ans += del(a[L]), L++;
            while(L > q[i].l) --L, Ans += add(a[L]);
            while(R < q[i].r) ++R, Ans += add(a[R]);
            while(R > q[i].r) Ans += del(a[R]), R--;
            ans[id] = Ans;
        }
        printf("Case #%d:\n", ++ks);
        for(int i = 1; i <= m; i++){
            printf("%lld\n", ans[i]);
        }
    }
    return 0;
}