zoukankan      html  css  js  c++  java
  • hdu 5381 The sum of gcd 2015多校联合训练赛#8莫队算法

    The sum of gcd

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
    Total Submission(s): 23    Accepted Submission(s): 4


    Problem Description
    You have an array A,the length of A is n
    Let f(l,r)=ri=lrj=igcd(ai,ai+1....aj)
     

    Input
    There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
    First line has one integers n
    Second line has n integers Ai
    Third line has one integers Q,the number of questions
    Next there are Q lines,each line has two integers l,r
    1T3
    1n,Q104
    1ai109
    1l<rn
     

    Output
    For each question,you need to print f(l,r)
     

    Sample Input
    2 5 1 2 3 4 5 3 1 3 2 3 1 4 4 4 2 6 9 3 1 3 2 4 2 3
     

    Sample Output
    9 6 16 18 23 10
     

    Source

    求一个区间内随意子区间的gcd之和。

    分析:

    对于区间[l,r]求gcd之和的复杂度是nlog(n)的

    ::

          如果处理了[l,r]的结果,那么对于[l,r+1]。能产生的新的子区间为[r-l+1]个。怎样合并?

          由于增加r+1,那么[L,r+1],(l<=L<=r)必定都是经过r位置的。知道r与之前每一个位置的gcd。

    用num[r+1]与这些gcd值,做gcd得到新的gcd值,就是全部新子区间的gcd结果。对于每一个gcd乘以相应的

    区间个数就可以。

           当然越往左。gcd就会越小,而且最多出现log个gcd值。把同样的gcd合并就能降低运算量。

    然后新增加的数自己能够成为一个区间,增加答案中。

    处理的复杂度是nlog的,由于分块了,

    长度为n的最多sqrt(n)段,复杂度是nlog(n)*sqrt(n)

    在块内。长度是sqrt(n)且最多次计算。全部是qlog(n)*sqrt(n)==================(log(n)是gcd的种类数)


    如今处理合并两段了:

             由于分成左右两段,例如以下

    原序列:1 1 1 2 2  2  4 4 4| 4 4 4 2 2 2 1 1 1 (|是分隔位置)

         gcd:1 1 1  2 2 2  4 4 4 | 4 4 4 2 2 2 1 1 1

    gcd计算的该点到切割位置的路径的gcd,由于合并肯定是须要经过切割位置的!

           显然能够知道gcd的种类仅仅有 log(n)个。对于左边的每一个gcd和右边的每一个gcd做一下gcd函数。然后乘以左边该段

    的长度*右边该段的长度。如样例就是

                gcd(1,1)*3*3+gcd(1,2)*3*3 + gcd(1,4)*3*3)...........+....

    怎样计算得到每一个gcd相应的区间个数呢?

                 能够知道从分隔线到两边的gcd是递减的。假设g是[l,r]的gcd,那么对于[l-1,r]仅仅要gcd(g,num[l-1])就计算出来了

                然后得到的gcd假设与g同样就和并。否则增加一个新值。

              复杂度分析:

                  对于每段,求出全部gcd和个数是o(n)的。

    长的一段最多求sqrt(n)次。是n*sqrt(n)的。短的是sqrt(n)*q次

    合并时复杂度是q*log(n)*log(n)的,所以是 O(sqrt(n)*n+q*sqrt(n)+q*log(n)*log(n))

    接下来看代码:




    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define maxn 20001
    #define ll long long
    
    int gcd(int a,int b){
        if(b == 0) return a;
        return gcd(b,a%b);
    }
    
    ll ans[maxn];
    struct Node{
        int l,r,id;
    };
    Node que[maxn];
    
    int length;//分块排序函数
    int comp(Node a,Node b){
        if(a.l / length == b.l / length)
            return a.r < b.r;
        return a.l /length < b.l/length;
    }
    int num[maxn];
    
    struct Point{
        int g,num;
        Point(int _g=0,int _n=0):g(_g),num(_n){}
    };
    vector<Point> lgcd;
    vector<Point> ltrgcd;
    vector<Point> rgcd;
    vector<Point> rtlgcd;
    
    int main(){
        int t,n,q;
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            for(int i = 0;i < n; i++)
                scanf("%d",num+i);
            scanf("%d",&q);
            for(int i = 0;i < q; i++){
                scanf("%d%d",&que[i].l,&que[i].r);
                que[i].id = i;
                que[i].l--,que[i].r--;
            }
    
            for(length = 1; length * length < n; length++);
            sort(que,que+q,comp);//按快排序,同一个块内r从小到大排序
    
            memset(ans,0,sizeof(ans));
            lgcd.clear();
            rgcd.clear();
            ltrgcd.clear();
            rtlgcd.clear();
    
            int RR = -1,kuai = -1,j,k,l,LL;
            ll res = 0,resl = 0;
            Point a;
            for(int i = 0;i < q; i++){ 处理每一个询问
                if(kuai != que[i].l/length){//新块。所以长的一段要重头開始处理
                    kuai = que[i].l/length;
                    rtlgcd.clear();
                    rgcd.clear();
                    RR = kuai*length+length-1;
                    res = 0;
                }
                while(RR < que[i].r){
                    RR++;//处理分隔线到RR的gcd之和。

    for( j = 0;j < rgcd.size(); j++){ rgcd[j].g = gcd(rgcd[j].g,num[RR]); res += (ll)rgcd[j].g*rgcd[j].num; } rgcd.push_back(Point(num[RR],1)); res += num[RR]; for(j = 0,k = 1;k<rgcd.size();k++){ if(rgcd[j].g == rgcd[k].g){ rgcd[j].num += rgcd[k].num; } else { j++; rgcd[j] = rgcd[k]; } } while(rgcd.size() > j+1) rgcd.pop_back();//合并同样的gcd //处理分隔线到每一个RR的gcd个数 if(rtlgcd.size() == 0) rtlgcd.push_back(Point(num[RR],1)); else { k = rtlgcd.size()-1;//仅仅需比較最小的那个gcd就可以。即最右边计算得到的gcd a.g = gcd(rtlgcd[k].g,num[RR]); a.num = 1; if(a.g == rtlgcd[k].g) rtlgcd[k].num++; else rtlgcd.push_back(a); } } LL = kuai*length+length-1; lgcd.clear(); ltrgcd.clear(); resl = 0;//左边的处理与右边的一样 LL = min(LL,que[i].r); for(;LL >= que[i].l; LL--){ for(j = 0;j < lgcd.size(); j++){ lgcd[j].g = gcd(lgcd[j].g,num[LL]); resl += (ll)lgcd[j].g*lgcd[j].num; } lgcd.push_back(Point(num[LL],1)); resl += num[LL]; for(j = 0,k=1;k<lgcd.size();k++){ if(lgcd[j].g == lgcd[k].g){ lgcd[j].num += lgcd[k].num; } else { j++; lgcd[j] = lgcd[k]; } } while(lgcd.size() > j+1) lgcd.pop_back(); if(ltrgcd.size() == 0){ ltrgcd.push_back(Point(num[LL],1)); } else { k = ltrgcd.size()-1; a.g = gcd(ltrgcd[k].g,num[LL]); a.num = 1; if(a.g == ltrgcd[k].g) ltrgcd[k].num++; else ltrgcd.push_back(a); } }//合并两个区间 ans[que[i].id] = res + resl; int id = que[i].id,gg; for(j = 0;j < ltrgcd.size(); j++){ gg = ltrgcd[j].g; for(k = 0;k < rtlgcd.size(); k++){ gg = gcd(gg,rtlgcd[k].g); ans[id] += (ll)gg*ltrgcd[j].num*rtlgcd[k].num; } } } for(int i = 0;i < q; i++){ printf("%I64d ",ans[i]); } } return 0; }



  • 相关阅读:
    docker 方式安装gitlab时,项目的clone地址及项目文件列表地址为机器名的问题解决办法
    CPU流水线
    Element中el-form嵌套el-table双击编辑提交检验
    java基础知识
    C#多线程下载
    mysql优化
    C++ 算法(一)
    前端vue 的面试总结 以及答案以及前端技术点面试
    C# 组合任务
    C# List去重DistinctBy扩展
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/6834568.html
Copyright © 2011-2022 走看看