[HDU 5875] Function (单调栈/(RMQ+二分))

链接

HDU 5875


题意

2016大连站网赛的1008。
给出一个长度为N的序列,和Q组询问,对每组询问[l, r],输出a[l] % a[l+1] % … % a[r],l等于r的时候输出a[l]即可。


题解

这题网上貌似有各种解法花式AC,这里给出两种。

第一种是预处理+暴力,对序列a,对a[i]求出其右侧第一个小于等于a[i]的值的下标,因为大于某个余数的值是没有意义的,在查询的时候可以加速计算,跳过一些不必要的值的取模。
但是这种方法的上界是O(n^2)的,比如一个很长的下降序列就可以轻松T掉这种做法,但是在这题里可以<1s AC,效率还是很高的(其实有点坑,早知道这么能过我们也这么写了)。

第二种做法是RMQ+二分,st表维护序列a的最小值,在处理某个查询的时候二分区间,可以查到区间小于等于某值的第一个值(最左侧)的下标,取模后继续查询直到区间右端点即可。
这种做法的复杂度是n*logn*logn的,每次二分区间寻找下一个小于等于该值的值需要logn,一个数在取模的过程中至少降低一半,这个衰减比logn还快,加上区间不停缩短,所以O(n*logn*logn)绝对是充裕的。
值的一提的是我这么做竟然T了,原因是我二分区间的函数是以递归形式写的,我改成迭代形式以后就过了,可见优化常数的重要性啊。。。


代码

单调栈预处理+暴力:

#include <cstdio>
#include <iostream>
using namespace std;
#define next Next
#define maxn (100010)
int a[maxn], next[maxn];
struct _node
{
    int idx, value;
} _stack[maxn];
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        int N;
        cin >> N;
        for(int i = 1, pre = -1; i <= N; i++)
        {
            scanf("%d", &a[i]);
            next[i] = 0;
        }

        for(int i = 1, top = 0; i <= N; i++)
        {
            while(top && _stack[top-1].value >= a[i])
            {
                next[_stack[top-1].idx] = i;
                top--;
            }
            _stack[top].idx = i;
            _stack[top++].value = a[i];
        }

        int M;
        cin >> M;
        for(int i = 0, l, r; i < M; i++)
        {
            scanf("%d%d", &l, &r);
            if(l == r) printf("%d\n", a[l]);
            else
            {
                int now = l, o = a[l];
                while(now < r)
                {
                    now = next[now];
                    if(!now || now > r) break;
                    o %= a[now];
                    if(!o) break;
                }
                printf("%d\n", o);
            }
        }
    }
    return 0;
}

这里写图片描述

RMQ(st表)+二分:

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
#define log2    Log2
#define maxn (100010)
int log2[maxn], st[maxn][32];
void st_prepare(int n, int *arr)
{
    log2[1] = 0;
    for(int i = 2; i <= n; i++)
    {
        log2[i] = log2[i-1];
        if(i == (1 << log2[i] + 1)) log2[i]++;
    }
    for(int i = n-1; i >= 0; i--)
    {
        st[i][0] = arr[i];
        for(int j = 1; i + (1 << j) - 1 < n; j++)
        {
            st[i][j] = min(st[i][j-1], st[i + (1 << j - 1)][j-1]);
        }
    }
}
inline int st_query(int l, int r)
{
    int k = log2[r - l + 1];
    return min(st[l][k], st[r - (1 << k) + 1][k]);
}
int st_find(int l, int r, int a)
{
    int m;
    while(l <= r)
    {
        if(l == r) return st[l][0] <= a ? l : 0;
        m = (l + r) >> 1;
        if(st_query(l, m) <= a) r = m;
        else if(st_query(m+1, r) <= a) l = m+1;
        else return 0;
    }
    return 0;
/*
    if(l == r) return st[l][0] <= a ? l : 0;

    int m = (l + r) >> 1, ret = 0;
    if(ret = st_find(l, m, a)) return ret;
    return st_find(m + 1, r, a);
*/
}
int a[maxn];
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        int N;
        cin >> N;
        for(int i = 1; i <= N; i++)
            scanf("%d", &a[i]);
        st_prepare(N + 1, a);
        int M, l, r, p, q, m, o;
        cin >> M;
        while(M--)
        {
            scanf("%d%d", &l, &r);
            if(l == r) { printf("%d\n", a[l]); continue; }
            p = l + 1, q = r, o = a[l];
            while(p <= q)
            {
                m = st_find(p, q, o);
                if(!m) break;
                o %= a[m];
                if(!o) break;
                p = m + 1;
            }
            printf("%d\n", o);
        }
    }
    return 0;
}

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值