• 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;
    }
  • 相关阅读:
    [原]UEFI+GPT启动VHD
    [原]procexp替换任务管理器
    [原]调试实战——使用windbg调试崩溃在ole32!CStdMarshal::DisconnectSrvIPIDs
    [转]Part2: Understanding !PTE, Part2: Flags and Large Pages
    [转]Part 3: Understanding !PTE
    [原]线性地址到物理地址转换后记
    [转]Part1: Understanding !PTE , Part 1: Let’s get physical
    [原]线性地址到物理地址转换
    [原]调试实战——使用windbg调试崩溃在ComFriendlyWaitMtaThreadProc
    [原]ComFriendlyWaitMtaThreadProc
  • 原文地址:https://www.cnblogs.com/clno1/p/11600392.html
Copyright © 2020-2023  润新知