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

来源:互联网 发布:淘宝客服售前问题用语 编辑:程序博客网 时间:2024/06/11 16:59

链接

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
原创粉丝点击