hdu 5875

来源:互联网 发布:win10家庭版网络共享 编辑:程序博客网 时间:2024/06/05 01:14

讨论

单调栈,构图,还有预处理,显然朴素的思想不可能过的,需要一点优化,可以发现,如果某数先对一个较小的数取模,然后对一个较大的数取模,则对较大的数取模并不会影响结果,于是可想到令每次取模的操作数严格递减,基于如此思路,可以构造一张图,前面较大的数指向后面第一个比他小的数,没有更小数的就不用向后指了,因而每个数最多只有一条出边,透过这张图加速取模过程,不过这顶多算剪枝,因为复杂度仍然是平方级,但已经足以应付这道题了
在实现方面,利用单调栈完成构图工作,由于序列要递减的,因而栈是非严格递增的,栈中有一个初始元素-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];//原始数组 单调栈及其栈顶 数指向的下一个数 也就是nextvoid 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

0 0
原创粉丝点击