题目大意
动态维护数列中大于等于某个数的极长连续段的个数。
思路
我们考虑每段的开头,记为 (i),高度为 (a_i)
那么此时水淹的高度必然满足 (a_{i-1} < x leq a_i)
这样的 (x) 在此处会给答案贡献加一
那么我们考虑所有的这种位置,开一棵权值线段树,对于这些 (x) 的位置都加 (1)
询问时就单点查询 (x) 在线段树种的值就行了
修改时注意它原来的贡献,包括它与前面的数和它与后面的数
这些减去,修改后在加上新的贡献
然后,就没有然后了······
(Code)
#include<cstdio>
#define ls (k << 1)
#define rs (ls | 1)
using namespace std;
const int N = 5e5;
int n , m , a[N + 5] , last;
struct seg{
int sum , tag;
}t[4 * N + 5];
void pushdown(int k , int l , int r)
{
if (!t[k].tag) return;
int m = (l + r) >> 1;
t[ls].sum += (m - l + 1) * t[k].tag , t[ls].tag += t[k].tag;
t[rs].sum += (r - m) * t[k].tag , t[rs].tag += t[k].tag;
t[k].tag = 0;
}
void pushup(int k){t[k].sum = t[ls].sum + t[rs].sum;}
void update(int l , int r , int k , int x , int y , int v)
{
if (x <= l && r <= y)
{
t[k].sum += (r - l + 1) * v;
t[k].tag += v;
return;
}
pushdown(k , l , r);
int mid = (l + r) >> 1;
if (x <= mid) update(l , mid , ls , x , y , v);
if (y > mid) update(mid + 1 , r , rs , x , y , v);
pushup(k);
}
int query(int l , int r , int k , int x)
{
if (l == r && l == x) return t[k].sum;
pushdown(k , l , r);
int mid = (l + r) >> 1;
if (x <= mid) return query(l , mid , ls , x);
else return query(mid + 1 , r , rs , x);
}
int main()
{
freopen("patrick.in" , "r" , stdin);
freopen("patrick.out" , "w" , stdout);
scanf("%d%d" , &n , &m);
for(register int i = 1; i <= n; i++)
{
scanf("%d" , a + i);
if (a[i] > N) a[i] = N;
if (a[i] > a[i - 1]) update(1 , N , 1 , a[i - 1] + 1 , a[i] , 1);
}
char opt[5]; int x , y;
for(register int i = 1; i <= m; i++)
{
scanf("%s" , opt);
if (opt[0] == 'Q')
{
scanf("%d" , &x) , x ^= last;
last = query(1 , N , 1 , x);
printf("%d
" , last);
}
else {
scanf("%d%d" , &x , &y) , x ^= last , y ^= last;
if (a[x] > a[x - 1]) update(1 , N , 1 , a[x - 1] + 1 , a[x] , -1);
if (a[x + 1] > a[x]) update(1 , N , 1 , a[x] + 1 , a[x + 1] , -1);
a[x] = y;
if (a[x] > a[x - 1]) update(1 , N , 1 , a[x - 1] + 1 , a[x] , 1);
if (a[x + 1] > a[x]) update(1 , N , 1 , a[x] + 1 , a[x + 1] , 1);
}
}
}