题意:N个高度为hi的果子,摘果子的个数是从位置1开始从左到右的严格递增子序列的个数。有M次操作,每次操作对初始序列修改位置p的果子高度为q。每次操作后输出修改后能摘到得数目。
分析:将序列分为左、右两部分,每次修改之后的结果是p左部到p递增的子序列长度,加上右部第一个高度大于max(q,p位置之前最大的高度)位置开始的递增子序列长度。
左部的查询可以线性递推预处理得到,右部的查询需借助ST表预处理出区间最大值,并二分求得位置。
#include<bits/stdc++.h> using namespace std; const int MAXN = 200007; int N, m, tot; int h[200005]; int st[MAXN][32-__builtin_clz(MAXN)]; int dp[200005]; int sel[200005], pre[200005], c[200005]; void init_st() { int l = 31 - __builtin_clz(N); for(int i=0;i<N;++i) st[i][0] = h[i]; for(int j=0;j<l;++j) for (int i=0;i<1+N-(1<<j);++i) st[i][j+1] = max(st[i][j], st[i+(1<<j)][j]); } int rmq(int l, int r) { int k = 31 - __builtin_clz(r - l + 1); return max(st[l][k], st[r-(1<<k)+1][k]); } int getbignext(int pos, int val) { int l = pos + 1, r = N, mid; while (r > l) { mid = (l + r) / 2; if (rmq(pos+1, mid) <= val) l = mid + 1; else r = mid; } return l; } void initdp() { int cnt = 1; dp[N] = 0; for (int i = N - 1; i >= 0; i--) { int pos = getbignext(i, h[i]); dp[i] = dp[pos] + 1; } sel[0] = 1; pre[0] = -1; c[0] = 1; int last = 0, lasth = h[0]; for (int i = 1; i < N; i++) { pre[i] = last; if (h[i] > lasth){ sel[i] = 1; last = i; lasth = h[i]; cnt++; } else sel[i] = 0; c[i] = cnt; } tot = cnt; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif int T;scanf("%d",&T); while(T--){ int Q; scanf("%d%d",&N,&Q); for(int i=0;i<N;++i) scanf("%d",&h[i]); init_st(); initdp(); int p,q; while(Q--){ scanf("%d%d",&p,&q); p--; int res=0; if(sel[p]){ //这个位置保持递增 if(p==0){ res = dp[getbignext(p,q)]+1; } else{ if(q>h[pre[p]]){ res += c[p]; res += dp[getbignext(p,q)]; } else{ res += c[pre[p]]; res += dp[getbignext(p,h[pre[p]])]; } } } else{ if(q<=h[pre[p]]){ res= tot; } else{ res += c[pre[p]]+1; res += dp[getbignext(p,q)]; } } printf("%d ",res); } } return 0; }