HDU 5726 GCD

对于第一问:无修改的查询区间GCD,可以采用RMQ倍增的思想。

第二问:可以预处理。暴力枚举左端点L。GCD从左到右是递减的,并且肯定是有一些段是一样的值,值的种类最多只有log(1000, 000, 000)种,因此可以二分确定每一段的范围。然后用map统计一下即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
typedef long long LL;

const int maxn=100000+10;
int T,n,a[maxn],dp[maxn][30];
map<int,LL>M;

int gcd(int a,int b) { if(b==0) return a; return gcd(b,a%b); }

void RMQ_init()
{
    for(int i=0;i<n;i++) dp[i][0]=a[i];
    for(int j=1;(1<<j)<=n;j++)
        for(int i=0;i+(1<<j)-1<n;i++)
            dp[i][j]=gcd(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}

int RMQ(int L,int R)
{
    int k=0;
    while((1<<(k+1))<=R-L+1) k++;
    return gcd(dp[L][k],dp[R-(1<<k)+1][k]);
}

void pre()
{
    M.clear();
    for(int i=0;i<n;i++)
    {
        int p=i;
        while(p<n)
        {
            int ll=p,rr=n-1,pos;
            while(ll<=rr)
            {
                int mid=(ll+rr)/2;
                if(RMQ(i,mid)<RMQ(i,p)) rr=mid-1;
                else pos=mid,ll=mid+1;
            }
            M[RMQ(i,p)]=M[RMQ(i,p)]+(LL)(pos-p+1);
            p=pos+1;
        }
    }
}

int main()
{
    scanf("%d",&T); int cas=0;
    while(T--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%d",&a[i]);
        RMQ_init(); pre();
        int q; scanf("%d",&q);
        printf("Case #%d:\n",++cas);
        for(int i=1;i<=q;i++)
        {
            int L,R; scanf("%d%d",&L,&R); L--,R--;
            printf("%d %lld\n",RMQ(L,R),M[RMQ(L,R)]);
        }
    }
    return 0;
}

 

posted @ 2016-07-21 22:18  Fighting_Heart  阅读(250)  评论(0编辑  收藏  举报