【HDOJ 5726】GCD(RMQ+二分)

Problem Description

Give you a sequence of N(N≤100,000) integers : a1,...,an(0<ai≤1000,000,000). There are Q(Q≤100,000) queries. For each query l,r you have to calculate gcd(al,,al+1,...,ar) and count the number of pairs(l′,r′)(1≤l<r≤N)such that gcd(al′,al′+1,...,ar′) equal gcd(al,al+1,...,ar).

Input

The first line of input contains a number T, which stands for the number of test cases you need to solve.

The first line of each case contains a number N, denoting the number of integers.
The second line contains N integers, a1,...,an(0<ai≤1000,000,000).
The third line contains a number Q, denoting the number of queries.
For the next Q lines, i-th line contains two number , stand for the li,ri, stand for the i-th queries.

Output

For each case, you need to output “Case #:t” at the beginning.(with quotes, t means the number of the test case, begin from 1).

For each query, you need to output the two numbers in a line. The first number stands for gcd(al,al+1,...,ar) and the second number stands for the number of pairs(l′,r′) such that gcd(al′,al′+1,...,ar′) equal gcd(al,al+1,...,ar).

Sample Input

1
5
1 2 4 6 7
4
1 5
2 4
3 4
4 4

Sample Output

Case #1:
1 8
2 4
2 4
6 1 

题意:

有N个数,求第L个数到第R个数的最大公约数,并且求出任意区间内最大公约数为ans的数量。

题解:

对于区间[L,R],如果L固定不变,R不断右移时,gcd的值在不断下降,而且每次下降的幅度都不小于一半。我们只要枚举左端点L,然后二分L到N的区间,找到最大公约数为gcd的最大区间,记录这个gcd的数量并更新到map中即可。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
typedef long long ll;
const int MAX=100005;
int dp[MAX][20];
int mm[MAX];
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}

void initrmq(int n,int b[])
{
    mm[0]=-1;
    for(int i=1;i<=n;i++)
    {
        mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
        dp[i][0]=b[i];
    }
    for(int j=1;j<=mm[n];j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            dp[i][j]=gcd(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
ll rmq(int x,int y)
{
    int k=mm[y-x+1];
    return gcd(dp[x][k],dp[y-(1<<k)+1][k]);
}

ll find(int l,int r)
{
    int k=(int)log2((double)(r-l+1));
    return gcd(dp[l][k], dp[r-(1<<k)+1][k]);
}

map<int,long long>ma;
int main()
{
    ios::sync_with_stdio(false);
    int T,n,ca=1,i,j;
    cin>>T;
    while(T--)
    {
        ma.clear();
        int b[MAX];
        cin>>n;
        for(i=1;i<=n;i++)
            cin>>b[i];

        initrmq(n,b);
        for(i=1;i<=n;i++)
        {
            int I=i;
            ll now=b[i];
            while(I!=n+1)
            {
                int preI=I,N=n;
                while(I!=N)
                {
                    int mid=(I+N)/2+1;
                    if(find(I,mid)==now)
                        I=mid;
                    else N=mid-1;
                }
                ma[now]+=N-preI+1ll;
                I++;
                if(I!=n+1)
                    now=gcd(now,b[I]);
            }
        }
        cout<<"Case #"<<ca++<<":"<<endl;
        int q;
        cin>>q;
        for(i=1;i<=q;i++)
        {
            int l,r;
            cin>>l>>r;
            ll ans=rmq(l,r);
            cout<<ans<<" "<<ma[ans]<<endl;
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/kannyi/p/9801346.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值