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 ,the
length of  is

Let

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

Second line has  integers

Third line has one integers ,the
number of questions

Next there are Q lines,each line has two integers ,

Output

For each question,you need to print

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

2015 Multi-University Training Contest 8

求一个区间内随意子区间的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\n",ans[i]);
        }
    }
    return 0;
}
时间: 2024-10-11 01:34:20

hdu 5381 The sum of gcd 2015多校联合训练赛#8莫队算法的相关文章

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&#39;s problem(manacher+二分/枚举)

HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分相同,第一部分与第二部分对称. 现在给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法,求出以第i个点为中心的回文串长度,记录到数组p中 要满足题目所要求的内容,需要使得两个相邻的回文串,共享中间的一部分,也就是说,左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也是一样. 因为我们已经记录下来以

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&amp;#39;s problem(manacher+二分/枚举)

pid=5371">HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分同样,第一部分与第二部分对称. 如今给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法.求出以第i个点为中心的回文串长度.记录到数组p中 要满足题目所要求的内容.须要使得两个相邻的回文串,共享中间的一部分,也就是说.左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也

HDU 5358(2015多校联合训练赛1006) First One (区间合并+常数优化)

HDU 5358 题意: 求∑?i=1?n??∑?j=i?n??(?log?2??S(i,j)?+1)?(i+j). 思路: S(i,j) < 10^10 < 2^34,所以log2(S)+1的值只可能在1~35之间.由于log变化缓慢的函数特性,我们可以S(i,n)分作多个相同log值的区间来计算,用pos[i][j]预处理记录每个以i为起点,log(s)值为j的区间的右边界,即可优化至nlogn的复杂度. 主要是写起来比较难一些,一些细节比较纠结,一定思路理清后再写. ps.此题卡常数毫无

hdu 5358 First One 2015多校联合训练赛#6 枚举

First One Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 142    Accepted Submission(s): 37 Problem Description soda has an integer array a1,a2,-,an. Let S(i,j) be the sum of ai,ai+1,-,aj. No

hdu 5361 2015多校联合训练赛#6 最短路

In Touch Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 67    Accepted Submission(s): 11 Problem Description There are n soda living in a straight line. soda are numbered by  from left to ri

hdu 5381 The sum of gcd(线段树+gcd)

题目链接:hdu 5381 The sum of gcd 将查询离线处理,按照r排序,然后从左向右处理每个A[i],碰到查询时处理.用线段树维护,每个节点表示从[l,i]中以l为起始的区间gcd总和.所以每次修改时需要处理[1,i-1]与i的gcd值,但是因为gcd值是递减的,成log级,对于每个gcd值记录其区间即可.然后用线段树段修改,但是是修改一个等差数列. #include <cstdio> #include <cstring> #include <vector>

(预处理+莫队算法)HDU - 5381 The sum of gcd

题意: 一个长度为n的数列,m次查询L到R之间所有连续子序列的gcd之和. 分析: 很明显的莫队算法. 很明显发现了gcd是单调递减的,并且最多存在32个的性质. 想了很久,脑补了许多种方法来拉伸L和R,但是都有漏洞. 实际上,这道题还是比较复杂的.. 在思考的过程中,我没有充分利用gcd的递减性质. 这题其实这题有共通之处,至少在我的做法上是这样的. 可以发现,在R向右拉伸的过程中,增加的和只是从L到R+1中的每一个后缀的和. 向左则为减,L的移动同理. 那么我们只要提前预处理每个位置的前缀所

HDU 5381 The sum of gcd (2015年多校比赛第8场)

1.题目描述:点击打开链接 2.解题思路:本题利用莫队算法解决.由于是第一次学习这个算法,因此研究了比较长的一段时间才弄懂.首先,莫队算法解决的问题是无修改的离线区间查询问题.该算法实际上是由曼哈顿距离最小生成树演变来的,由于要处理m个区间,可以将这m个区间看做二维平面上的点,那么处理这m个区间就等价于让这m点连通,且总的转移代价最小.这其实就是一个曼哈顿距离最小生成树问题. 经典的曼哈顿距离最小生成树的时间复杂度是O(NlogN),莫队算法的时间复杂度是O(N^1.5).不过本题还有一个地方,

hdu 5381 The sum of gcd 莫队+预处理

The sum of gcd Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem Description You have an array A,the length of A is nLet f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj) Input There are multiple test cases. The first line