1. 程式人生 > >hdu 5381 The sum of gcd 2015多校聯合訓練賽#8莫隊算法

hdu 5381 The sum of gcd 2015多校聯合訓練賽#8莫隊算法

names 來看 efi nbsp span ems multipl script there

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; }



hdu 5381 The sum of gcd 2015多校聯合訓練賽#8莫隊算法