GCD (ST表,二分求区间查询)

HUD 5726 GCD

  

给一个序列,多次查询区间的最大公约数,并求出同样是这个最大公约数的区间有多少个。

区间查询采用ST表,第二问查询利用区间向右延伸最大公约数递减的规律可通过二分快速找到右边界。把第一问的答案先求出来,表示要查询的公约数加入map。

然后枚举左端点二分右端点,把已经从存在map的值加上找到的区间数,不存在的就不用管了。



#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<list>
#include<map>
#include<math.h>
//#include<Windows.h>

using namespace std;

#define min(a,b) (a < b ? a:b)
#define max(a,b) (a > b ? a:b)
#define LL long long int

int T, N, Q;
int dp[100005][17];
int qurey[100005][3];
map<int, LL> m;

int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}

int GCD(int l, int r)
{
    int flr = log2(r - l + 1), len = (1 << flr) ;
    return gcd(dp[l][flr], dp[r - len + 1][flr]);
}

int main()
{
    int Case = 0;

//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);

    scanf("%d", &T);

    while(T--)
    {
        ++Case;
        m.clear();
        scanf("%d", &N);

        for(int i = 0; i < N; ++i)
        {
            scanf("%d", &dp[i][0]);
        }

        for(int t = 1; (1 << t) <= N; ++t)
        {
            for(int i = 0; i + (1 << t) <= N; ++i)
            {
                dp[i][t] = gcd(dp[i][t - 1], dp[i + (1 << (t - 1))][t - 1]);
            }
        }

        scanf("%d", &Q);
        printf("Case #%d:\n", Case);
        int l, r;

        for(int i = 0; i < Q; ++i)
        {
            scanf("%d %d", &qurey[i][0], &qurey[i][1]);
            qurey[i][2] = GCD(qurey[i][0] - 1, qurey[i][1] - 1);
            m[qurey[i][2]] = 0;
        }

        for(int i = 0; i < N; ++i)
        {
            int j = i;

            while(j < N)
            {
                int d = GCD(i, j);
                int l = j, r = N - 1;

                while(l < r)
                {
                    int mid = (l + r + 1) >> 1;

                    if(GCD(i, mid) < d)
                    {
                        r = mid - 1;
                    }
                    else
                    {
                        l = mid;
                    }
                }

                if(m.find(d) != m.end())
                {
                    m[d] += (l - j + 1);
                }

                j = r + 1;
            }
        }

        for(int i = 0; i < Q; ++i)
        {
            printf("%d %lld\n", qurey[i][2], m[qurey[i][2]]);
        }
    }


//system("pause");
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值