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





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值