HDU 5726 GCD RMQ

GCD

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 681    Accepted Submission(s): 207


Problem Description
Give you a sequence of  N(N100,000)  integers :  a1,...,an(0<ai1000,000,000) . There are  Q(Q100,000)  queries. For each query  l,r  you have to calculate  gcd(al,,al+1,...,ar)  and count the number of pairs (l,r)(1l<rN) 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<ai1000,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
 

Author
HIT
 

Source
 

Recommend
wange2014   |   We have carefully selected several similar problems for you:   5733  5732  5731  5730  5729 


题意:给1e5个数,1e5个查询,查询 [L,R]区间所有数的gcd,以及这个gcd在整个a数组中有多少个(区间的gcd) == 这个gcd。


区间gcd,这个直接把以前线段树模版求和改成两个数的gcd就行。但是我tle了,改成RMQ,改进查询的复杂度。RMQ也是把求和改成求两个数gcd就行。


重点是求所有区间gcd个数。暴力枚举n方复杂度不行,可以发现一个区间内,越往后gcd是呈阶梯式减小的。最多减小log1000,000,000次。可以枚举每个数,二分查询递减的时候每个阶梯的位置,然后用一个map记录下+=这个阶梯gcd的长度。


CODE

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5+10;

int n;
LL dp[N][25];   ///以i开头的后面长度为2的j次方的gcd
LL a[N];        ///用于输入的数组
map<int,LL> mp; ///存gcd个数

LL gcd(LL a,LL b){
    return b == 0?a:gcd(b,a%b);
}

int ask(int l,int r)   ///RMQ查询
{
    int x=(int)(log(r-l+1)/log(2));
    return gcd(dp[l][x],dp[r-(1<<x)+1][x]);
}


void ST()               ///RMQ
{
    for(int i = 1;i <= n;i++)
    {
        dp[i][0] = a[i];
    }
    for(int j = 1;j <= (int)(log(n)/log(2));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]);
        }
}

int main(void)
{
    int T;
    scanf("%d",&T);
    int times = 1;
    while(T--){
        mp.clear();
        scanf("%d",&n);
        for(int i = 1;i <= n;i++) scanf("%I64d",&a[i]);
        ST();
        for(int i = 1;i <= n;i++){   ///预处理区间gcd的个数
            int val = a[i];
            int pos = i;
            while(pos <= n){
                val = ask(i,pos);
                int l = pos,r = n;
                while(l <= r){
                    int mid = (l+r)/2;
                    if(ask(i,mid) == val) l = mid+1;
                    else                  r = mid-1;
                }
                mp[val] += r-pos+1;
                pos = l;
            }
        }
        printf("Case #%d:\n",times++);
        int qq;
        scanf("%d",&qq);
        while(qq--){
            int a,b;
            scanf("%d%d",&a,&b);
            LL tmp = ask(a,b);
            printf("%I64d %I64d\n",tmp,mp[tmp]);
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值