ST表 HDOJ5726 区间GCD

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5726

ST表适用于静态数据的查询(不支持修改),经过一次O(nlogn)的离线预处理之后,查询O(1)。

预处理过程运用了DP思想,st[i,j]表示从第i个数开始长度为2的j次方的区间中该区间的特征值(最值,GCD……)

状态转移方程:
st[i,0] = num[i]
st[i, j] = f(st[i][j-1], st[i + (1 << (j - 1)), j - 1])

查询区间为[l, r],那么可以找到一个k,使得2的k+1次方大于区间长度而2的k次方小于等于区间长度,那么就可以从st[left, k]和st[right - (1<

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
    const int MAX = 100010;
    int st[MAX][20];
    int a[MAX];
    int t,n,m;
    map<int, long long> mp;
int gcd(int a, int b)
{
    int r;
    while (b) {
        r = a % b;
        a = b;
        b = r;
    }
    return a;
}
void build()
{
    for (int i = 1; i <= n; i ++) {
        st[i][0] = a[i];
    }
    for (int j = 1; (1 << j) <= n; j ++) {
        for (int i = 1; i + (1<<j) - 1 <= n; i ++) {
            st[i][j] = gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
        }
    }
}
int query(int l, int r)
{
    int k = 0;
    while ((1 << (k + 1)) <= (r - l + 1)) k ++;
    return gcd(st[l][k], st[r - (1 << k) + 1][k]);
}
void bin()
{
    for (int i = 1; i <= n; i ++) {
        int g = a[i];
        int j = i;
        while (j <= n) {
            int l = j;
            int r = n;
            int mid;
            while (l < r) {
                mid = (l+r+1)>>1;
                if (g == query(i,mid)) {
                    l = mid;
                } else {
                    r = mid - 1;
                }
            }
            mp[g] += (l - j + 1) ;
//          printf("%d %d %d %d %lld\n",i,l,j,g,mp[g]);
            j = l + 1;
            g = query(i,j);
        }
    }
}
int main()
{
    scanf("%d",&t);
    int tc = 0;
    while (t --) {
        mp.clear();
        memset(st,0,sizeof(st));
        scanf("%d",&n);
        for (int i = 1; i <= n; i ++) {
            scanf("%d",&a[i]);
        }
        build();
        bin();
//      for (auto it = mp.begin(); it != mp.end(); it ++) {
//          printf("%d %lld\n",it->first,it->second);
//      }
        scanf("%d",&m);
        printf("Case #%d:\n",++ tc);
        for (int i = 1; i <= m; i ++) {
            int l,r;
            scanf("%d%d",&l,&r);
            int g = query(l,r);
            printf("%d %I64d\n",g,mp[g]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值