HDU 5726 GCD

GCD


Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)



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
 
#include <set>
#include <map>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <bitset>
#include <string>
#include <vector>
#include <iomanip>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define maxn 100010
using namespace std;
int a[maxn];
int n, q;
int gcd(int a, int b) //最大公约数
{
        return b ? gcd(b, a % b) : a;
}
int dp[maxn][17];  //dp[i][j]表示i为起点,长度为1<<j的区间最大公约数的值
map<int, long long> mp; //记录某一公约数的次数
int RMQ(int l, int r) //查询
{
        int k = (int)log2(1.0 * (r - l + 1));
        return gcd(dp[l][k], dp[r - (1 << k) + 1][k]);
}
void init() //预处理
{
        for (int i = 1; i <= n; i++)
        {
                dp[i][0] = a[i];
        }
        for (int i = 1; i <= log2(n); i++)
        {
                for (int j = 1; j <= n; j++)
                {
                        if (j + (1 << i) - 1 <= n)
                        {
                                dp[j][i] = gcd(dp[j][i - 1], dp[j + (1 << (i - 1))][i - 1]);
                        }
                }
        }
        for (int i = 1; i <= n; i++) //更新map
        {
                int g = dp[i][0], j = i;
                while (j <= n) //记录以i为断点的所有区间的gcd
                {
                        int l = j, r = n;
                        while (l < r) //找到公约数刚发生变化时的坐标 l
                        {
                                int mid = (l + r + 1) >> 1;
                                if (RMQ(i, mid) == g)
                                {
                                        l = mid;
                                }
                                else
                                {
                                        r = mid - 1;
                                }
                        }
                        mp[g] += l - j + 1; //l与j之间的坐标与左端点i构成的区间公约数都为g
                        j = l + 1; //接着找下一次变化
                        g = RMQ(i, j);
                }
        }
}
int main()
{
        int t;
        scanf("%d", &t);
        for (int tt = 1; tt <= t; tt++)
        {
                mp.clear();
                scanf("%d", &n);
                for (int i = 1; i <= n; i++)
                {
                        scanf("%d", &a[i]);
                }
                init();
                printf("Case #%d:\n", tt);
                scanf("%d", &q);
                while (q--)
                {
                        int l, r;
                        scanf("%d %d", &l, &r);
                        int ans=RMQ(l,r);
                        printf("%d %lld\n",ans, mp[ans]);
                }
        }
        return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值