hdu 5381 莫隊算法/gcd

The sum of gcd

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


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
 

Author
SXYZ
 

Source
 


 
區間查詢問題,考慮離線的做法。這題關鍵點是,假如已知k個數a1,...,ak的gcd,那麼在原來k個數基礎上加多一個數,也就是a1,...ak+1,k+1個數的gcd,要麼不變,要麼就是至少少了一個因數,而最小的因數就是2,相當於至少變爲原來的一半。也就是說1-n個數的gcd最多變了logn次。那麼到以第i個數結尾的gcd前面最多可以分爲logn段,每一段內gcd相同。有了這個能在logn時間內求出第i個數對前面任意以i結尾的區間的答案的貢獻。也是就是知道了[l,r],能在logn內知道[l,r+1]和[l,r-1]。這個時候就可以用莫隊算法了。

在網上找了很多莫隊算法的資料,感覺就這個最好懂Mo's algorithm  

這場多校是中學生出的。。。應該是之前中學生被人抱怨出題太難,所以這題數據範圍放小了,常數小的O(n^2)算法就能800ms水過。。(每次處理出第r個數的logn段gcd區間直接向前遍歷一遍更新所有[i,r]的答案),莫隊算法只用180ms


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
typedef pair<int, int> P;
typedef long long LL;
#define fir first
#define sec second
const int maxn=1e5+10;

int n,m,a[maxn];
int siz,num;

struct Query
{
    int l, r, id;
    int st;
    bool operator < (const Query& a) const
    {
        return st!=a.st ? st<a.st : r<a.r;
    }
};

Query q[maxn];
vector<P> seg[maxn], segr[maxn];

int gcd(int a, int b)
{
    int tmp;
    while(b){
        tmp=a%b;
        a=b;
        b=tmp;
    }
    return a;
}

LL ans[maxn];

int main()
{
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=0; i<=n; i++) seg[i].clear(), segr[i].clear();
        for(int i=0; i<n; i++) scanf("%d", a+i);
        siz=sqrt(n);
        num=(n+siz-1)/siz;

        cin>>m;
        for(int i=0; i<m; i++){
            scanf("%d%d", &q[i].l, &q[i].r);
            q[i].l--; q[i].r--;
            q[i].id=i;
            q[i].st=q[i].l/siz;
        }
        sort(q, q+m);


        for(int i=0; i<n; i++){
            if(i)
            for(int j=0; j<seg[i-1].size(); j++){
                int v=gcd(seg[i-1][j].sec, a[i]);
                int tot=seg[i].size();
                if(tot && seg[i][tot-1].sec==v)
                    seg[i][tot-1].fir=seg[i-1][j].fir;
                else
                    seg[i].push_back(P(seg[i-1][j].fir, v));
            }
            int tot=seg[i].size();
            if(tot && seg[i][tot-1].sec==a[i])
                seg[i][tot-1].fir=i;
            else seg[i].push_back(P(i, a[i]));
        }

        for(int i=n-1; i>=0; i--){
            for(int j=0; j<segr[i+1].size(); j++){
                int v=gcd(segr[i+1][j].sec, a[i]);
                int tot=segr[i].size();
                if(tot && segr[i][tot-1].sec==v)
                    segr[i][tot-1].fir=segr[i+1][j].fir;
                else
                    segr[i].push_back(P(segr[i+1][j].fir, v));
            }
            int tot=segr[i].size();
            if(tot && segr[i][tot-1].sec==a[i])
                segr[i][tot-1].fir=i;
            else segr[i].push_back(P(i, a[i]));
        }

        int l=0,r=0;
        LL val=a[0];
        for(int i=0; i<m; i++){
            while(r<q[i].r){
                r++;
                for(int j=seg[r].size()-1; j>=0; j--){
                    if(j &&l<=seg[r][j-1].first){
                        val+=(LL)seg[r][j].sec*(seg[r][j].fir-seg[r][j-1].fir);
                    }
                    else{
                        val+=(LL)seg[r][j].sec*(seg[r][j].fir-l+1);
                        break;
                    }
                }
            }

            while(r>q[i].r){
                for(int j=seg[r].size()-1; j>=0; j--){
                    if(j &&l<=seg[r][j-1].fir){
                        val-=(LL)seg[r][j].sec*(seg[r][j].fir-seg[r][j-1].fir);
                    }
                    else{
                        val-=(LL)seg[r][j].sec*(seg[r][j].fir-l+1);
                        break;
                    }
                }
                r--;
            }

            while(l<q[i].l){
                for(int j=segr[l].size()-1; j>=0; j--){
                    if(j &&r>=segr[l][j-1].fir){
                        val-=(LL)segr[l][j].sec*(segr[l][j-1].fir-segr[l][j].fir);
                    }
                    else{
                        val-=(LL)segr[l][j].sec*(r-segr[l][j].fir+1);
                        break;
                    }
                }
                l++;
            }

            while(l>q[i].l){
                l--;
                for(int j=segr[l].size()-1; j>=0; j--){
                    if(j &&r>=segr[l][j-1].fir){
                        val+=(LL)segr[l][j].sec*(segr[l][j-1].fir-segr[l][j].fir);
                    }
                    else{
                        val+=(LL)segr[l][j].sec*(r-segr[l][j].fir+1);
                        break;
                    }
                }
            }
            ans[q[i].id]=val;

        }

        for(int i=0; i<m; i++)
            printf("%I64d\n", ans[i]);

    }
    return 0;
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章