题目链接
西南科技大学第十三届程序设计竞赛G题
Description
小ZZ十分喜欢吃糖果, 于是他购买了nn颗糖果(从左到右放成一排,编号 1−n1−n ),每颗糖果都有一个甜度值wiwi 。
小ZZ由于吃了太多的糖果,以至于只有当他吃了比之前吃的糖果都甜的糖果时(wnow>wprewnow>wpre),他才感觉到 "吃" 了一颗糖果。
为了更有效的 "吃" 糖果,他想知道当他从第 ll 颗糖吃到第 rr 颗糖果,他 "吃" 了多少颗糖果(可以认为在吃第 ll 颗糖果之前他吃了一颗甜度值为 00 的糖果,不计入答案)。
Input
第一行包含一个整数 T (1≤T≤1000)T (1≤T≤1000) 代表测试组数,对于每一组测试:
第一行两个整数 nn,qq (1≤n≤105,1≤q≤105)(1≤n≤105,1≤q≤105) 分别表示糖果的颗数,询问的次数(每次询问独立,即每次询问的操作都是在最初的糖果上进行的)。
第二行 nn 个整数 wi (1≤wi≤n)wi (1≤wi≤n),表示糖果的甜度值。
接下来 qq 行,每一行两个整数 li,ri (1≤li≤ri≤n)li,ri (1≤li≤ri≤n) 表示从第 lili 颗糖吃到第 riri 颗糖果。
数据保证所有测试的 n,qn,q 的和不会超过 106106。
Output
对于每组测试,输出 qq 行,每行包含一个整数,表示小ZZ从第 lili 颗糖吃到第 riri 颗糖果, "吃" 的糖果 的数量。
1 6 3 2 1 3 5 4 5 1 6 2 5 5 6
3 3 2
Hint
小ZZ从第 11 颗吃到第 66 颗时:"吃" 了第 1,3,41,3,4 颗糖果。
小ZZ从第 22 颗吃到第 55 颗时:"吃" 了第 2,3,42,3,4 颗糖果。
小ZZ从第 55 颗吃到第 66 颗时:"吃" 了第 5,6,5,6, 颗糖果。
有点类似于REQ这道题的思路。
的确是一道很不错的题,有大佬用LCA的倍增去写,2000+ms,不如写树状数组来的快,1000-ms即可。
但是树状数组的确需要些思维,想了好久才想明白,也怪不得比赛的时候没能做出来,我们从右区间降序的角度去看,从右往左去维护关系。
先使用单调栈用来维护一下,第一个大于等于该点的点的所在的位置,然后这样子就建立了关系,同样的,这个点被指向了,就代表着后面有比它小的节点,所以,当我们的区间包含它的时候,需要这样的减去对应的值,同样的,当右区间缩过来的时候,需要将指向的点把值给加回去“+1”,这样就是把关系还回去并且推出的意思了。
可以去写写REQ这个问题,会理解的更加清晰(那道题更难些,但是个经典问题)。
最后,举个例子方便大家理解:
我给出的数为(9个):5、7、6、2、6、6、2、7、8
先看看挪动左端点,
对应的,我们从第9个开始,树状数组+1开始;
到第8个,发现后面没有比它"≥"的,那么继续+1;
同理,第7个;
但是对于第6个,是2(第7个)的对应的第一个"≤"那么,就+1-1变成了"+0",不做变化了;
对于第5个,被(6)第6个的对应的"≤"那么,也同样的是+1-1;
对于第4个,没有被后面的所指向(不是左边的第一个"≥"即可),直接+1就是了;
第3个的时候,会被后面的2(第4个)、6(第5个)所指向,所以变成了+1-2,是-1的更新;
第2个,-1,……类推;
然后,我们就要挪动右端点了,
挪动第9个的时候,前面没有比它大的,不考虑了;
挪动第8个的时候,给予第2个(7值)上的进行+1,也就是还回去的意思了;
挪动第7个的时候,给予第6个+1;
……
以此类推,还给所指向的"≥"它的节点一个数即可。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define esp 1e-6
#define INF 0x3f3f3f3f3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 1e5 + 7;
int N, Q, a[maxN], trie[maxN], ans[maxN];
struct Question
{
int l, r, id;
Question(int a=0, int b=0, int c=0):l(a), r(b), id(c) {}
}op[maxN];
bool cmp(Question e1, Question e2) { return e1.r > e2.r; }
inline void update(int i, int x)
{
if(i == 0) return;
while(i <= N)
{
trie[i] += x;
i += lowbit(i);
}
}
inline int query(int i)
{
int ans = 0;
while(i)
{
ans += trie[i];
i -= lowbit(i);
}
return ans;
}
struct node
{
int id, val;
node(int a=0, int b=0):id(a), val(b) {}
};
stack<node> st;
int to[maxN]; //上一个比它大的节点的所在位置,为0就是没了
int behind[maxN]; //在下一个比它大的数之前,有几个数比它小或者等于
inline void init()
{
for(int i=1; i<=N; i++) trie[i] = behind[i] = 0;
while(!st.empty()) st.pop();
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
scanf("%d%d", &N, &Q);
for(int i=1; i<=N; i++) scanf("%d", &a[i]);
for(int i=1; i<=Q; i++) { scanf("%d%d", &op[i].l, &op[i].r); op[i].id = i; }
sort(op + 1, op + Q + 1, cmp);
init();
to[1] = 0;
st.push(node(1, a[1]));
for(int i=2; i<=N; i++)
{
node tmp;
to[i] = 0;
while(!st.empty())
{
tmp = st.top();
if(tmp.val >= a[i])
{
to[i] = tmp.id;
//behind[tmp.id] = i - tmp.id;
behind[tmp.id]++;
break;
}
else st.pop();
}
st.push(node(i, a[i]));
}
int l = N, r = N;
for(int q=1; q<=Q; q++)
{
while(r > op[q].r)
{
update(to[r], 1);
r--;
}
while(l >= op[q].l)
{
update(l, -behind[l] + 1);
l--;
}
ans[op[q].id] = query(op[q].r) - query(op[q].l - 1);
}
for(int i=1; i<=Q; i++) printf("%d\n", ans[i]);
}
return 0;
}
/*
1
9 7
5 7 6 2 6 6 2 7 8
6 9
5 7
9 9
1 4
5 9
4 7
2 9
*/