hdu 5875

讨论

单调栈,构图,还有预处理,显然朴素的思想不可能过的,需要一点优化,可以发现,如果某数先对一个较小的数取模,然后对一个较大的数取模,则对较大的数取模并不会影响结果,于是可想到令每次取模的操作数严格递减,基于如此思路,可以构造一张图,前面较大的数指向后面第一个比他小的数,没有更小数的就不用向后指了,因而每个数最多只有一条出边,透过这张图加速取模过程,不过这顶多算剪枝,因为复杂度仍然是平方级,但已经足以应付这道题了
在实现方面,利用单调栈完成构图工作,由于序列要递减的,因而栈是非严格递增的,栈中有一个初始元素-INF,确保第一个数可以顺利压入,对此后遇到的每个数,将比他大的都弹出,然后将这个数压入,在弹出的过程中完成弹出的数的构图,因为弹出的都是比他大的数,而实际上,栈里实际存的并非数值,而是这些数在数组中的下标,这样每次弹出的时候可以立刻直到要将哪些位置的数连接
极端情况下,存在100000个数,从100000到1,这种情况下这个剪枝会显得毫无意义,而且会带来额外的开销,所幸并没有这种数据
其实还可以进一步剪枝,将起点相同但终点不同的查询按查询区间长度排序,这样可以直接记录下较短查询区间的结果,然后在较长查询区间中直接使用,但是对于起点不同的不能用,因为取模不满足交换律
虽说数据中存在0,但是题目确保了所有查询的区间中不会模0,因而除非是查询的边界条件没控制好,应该是不会RE的

题解状态

670MS,2492K,841 B,C++

题解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 100005
#define memset0(a) memset(a,0,sizeof(a))

int N, Q;//点总数 查询总数
int nums[MAXN], stk[MAXN], top, nex[MAXN];//原始数组 单调栈及其栈顶 数指向的下一个数 也就是next
void fun()
{
    for (int p = 1; p <= N; p++) {
        scanf("%d", &nums[p]);//input
        while (nums[p] < (top ? nums[stk[top - 1]] : INT_MIN))//空栈时假设有个INT_MIN 在栈底
            nex[stk[--top]] = p;//弹掉所有较大的数并完成构图
        stk[top++] = p;//然后把这个数压栈
    }
    scanf("%d", &Q);//input
    for (int p = 0; p < Q; p++) {
        int l, r;
        scanf("%d%d", &l, &r);//input
        int ans = nums[l++];
        for (; l&&l <= r; l = nex[l])//既要存在更小的数 又不能超出右边界
            ans %= nums[l];
        printf("%d\n", ans);//output
    }
}
int main(void)
{
    //freopen("vs_cin.txt", "r", stdin);
    //freopen("vs_cout.txt","w",stdout);

    int times;
    while (~scanf("%d", &times)) {//input
        while (times--) {
            scanf("%d", &N);//input
            fun();
            top = 0;
            memset0(nex);
        }
    }
}

EOF

展开阅读全文
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值