HDU-4676 Sum Of Gcd 莫队+欧拉函数

题意:给定一个11~nn的全排列AA,若干个询问,每次询问给出一个区间[l,r][l,r],要求得出li<jr  gcd(Ai,Aj)的值。

解法:这题似乎做的人不是很多,蒟蒻当然不会做只能看题解了qwq,目前看到一个比较好的题解是https://blog.csdn.net/Maxwei_wzj/article/details/79355887

没什么好说的,首先必须先推式子。

 那么这一坨东西到底是什么意思呢?其实就是对于每个数d,sigma[d|ai][d|aj] 是从区间[l,r]中选两个数且都有约数d的方案数,然后phi(d)乘以这个方案数,对所有的d求和就是答案了。

然后问题是怎么求从区间[l,r]中选两个数且都有约数d的方案数?考虑[l,r]中的数存在约数d的数个数为c[d],那么显然方案数就是c[d]*(c[d]-1)/2。那么问题又变成了怎么快速维护[l,r]中的数存在约数d的数个数呢?对于这题只有询问的题目我们可以考虑使用莫队算法:先预处理每个数的约数,然后每增加/减少一个数就枚举它的所有约数计算贡献即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e4+10,B=233;
#define bel(x) ((x-1)/B+1)
int n,m,a[N]; LL sum,c[N],ans[N];
struct query{
    int id,l,r;
    bool operator < (const query &rhs) const {
        return bel(l)==bel(rhs.l) ? r<rhs.r : l<rhs.l;  //询问排序顺序 
    }
}q[N];

bool notp[N];
int pnum, p[N], phi[N];
vector<int> fac[N];
void prework(int n) {
    memset(notp, 0, sizeof notp); pnum = 0;
    phi[1]=1;
    for (int i = 2; i <= n; i++) {
        if (!notp[i]) {
            p[pnum++] = i;
            phi[i] = i - 1;
        }
        for (int j = 0; j < pnum && i * p[j] <= n; j++) {
            int k = i * p[j]; notp[k] = 1;
            if (i % p[j] == 0) {
                phi[k] = phi[i] * p[j];
                break;
            }
            phi[k] = phi[i] * (p[j] - 1);
        }
    }
    for (int i=1;i<=n;i++)
        for (int j=i;j<=n;j+=i)
            fac[j].push_back(i);
}

void add(int x) {  //添加操作 
    for (int i=0;i<fac[x].size();i++) {
        int y=fac[x][i];
        sum+=c[y]*phi[y];
        c[y]++;
    }
}

void del(int x) {  //删除操作 
    for (int i=0;i<fac[x].size();i++) {
        int y=fac[x][i];
        c[y]--;
        sum-=c[y]*phi[y];
    }
}

void solve() {  //莫队算法 
    int pl=1,pr=0; sum=0;
    for (int i=1;i<=m;i++) {
        while (pl<q[i].l) del(a[pl++]);
        while (pl>q[i].l) add(a[--pl]);
        while (pr<q[i].r) add(a[++pr]);
        while (pr>q[i].r) del(a[pr--]);
        ans[q[i].id]=sum;
    }
}

int main()
{
    prework(20000);
    int T,cas=0; cin>>T;
    while (T--) {
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        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);
        for (int i=1;i<=n;i++) c[i]=0;
        solve();
        printf("Case #%d:
",++cas);
        for (int i=1;i<=m;i++) printf("%lld
",ans[i]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/clno1/p/11600392.html