題目連接
思路:一開始看到這題毫無思路,賽後看了題解和通過的代碼,才瞭解到這個題的技巧。
顯然修改操作可以通過線段樹打標記實現。那麼難的就是查詢操作了。
考慮一種暴力的查詢:每次詢問的時候,我們把區間定在[x,n]上,然後類似dfs的一個過程在線段樹上搜,每次搜到葉子節點的時候 ,我們判斷[x,now]的最小值是不是等於now位置上的值。那這個複雜度顯然是不行的。
那我們考慮怎樣優化這個過程:
我們每次查詢的時候都會遞歸左右子樹,那有沒有一種只需遞歸一邊子樹的方法呢?
顯然是有的。
我們用一個數組g來記錄一段區間[l,r]中的x數量,其中x滿足:
1.l<=x<=r
2:t[x]=min(l~r)
顯然葉子節點的g值爲1,合併兩個子樹的時候顯然左子樹的答案是正確的,我們只需要另外計算一下右子樹的貢獻就行。
我們查詢的時候,需要保證合法的查詢區間的右端點是遞增的,那麼就要先遞歸左兒子再遞歸右兒子,然後我們維護一下前綴區間的最小值val,就可以計算這個區間的貢獻了。
區間貢獻計算方式:
顯然如果當前點爲葉子節點,那麼如果這個點的值小於等於val的時候貢獻爲1,否則爲0。
然後我們看這個區間的最小值如果不是小於等於val那麼這個區間顯然是不合法的,貢獻必然爲0。
反正我們看這個區間的左子樹的最小值是不是小於等於val的,如果左子樹的最小值小於等於val,那麼我們只需要遞歸處理左子樹,並加上右子樹的貢獻即可,因爲此時右子樹的g值必然是合法的,但是左子樹的g值不一定合法;如果左子樹的最小值不是小於等於val的我們就遞歸右子樹即可,因爲此時的左子樹的貢獻必然爲0。
這個方法很巧妙的維護了一個g數組,來做到查詢一個區間的貢獻的時候只花費了log的時間,總的查詢時間就是.
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
int n,m;
int a[N];
#define mid (l+r>>1)
#define ls o<<1
#define rs o<<1|1
int t[N<<2],laz[N<<2];
int g[N<<2];
void pd(int o,int l,int r){
if(laz[o]){
laz[ls]+=laz[o];
laz[rs]+=laz[o];
t[ls]+=laz[o];
t[rs]+=laz[o];
laz[o]=0;
}
}
int merge(int o,int l,int r,int x){
//找到這個區間
if(l==r)return t[o]<=x;
pd(o,l,r);
if(t[o]>x)return 0;
if(t[ls]>x)return merge(rs,mid+1,r,x);
return merge(ls,l,mid,x)+g[o]-g[ls];
}
void push_up(int o,int l,int r){
t[o]=min(t[ls],t[rs]);
g[o]=g[ls]+merge(rs,mid+1,r,t[ls]);
}
void build(int o,int l,int r){
if(l==r){
t[o]=a[l];
g[o]=1;
return ;
}
build(ls,l,mid);
build(rs,mid+1,r);
push_up(o,l,r);
}
void up(int o,int l,int r,int x,int y,int d){
if(l>=x&&r<=y){
laz[o]+=d;
t[o]+=d;
return ;
}
pd(o,l,r);
if(x<=mid)up(ls,l,mid,x,y,d);
if(y>mid)up(rs,mid+1,r,x,y,d);
push_up(o,l,r);
}
int getx(int o,int l,int r,int x){
if(l==r)return t[o];
pd(o,l,r);
int ans;
if(x<=mid)ans=getx(ls,l,mid,x);
else ans=getx(rs,mid+1,r,x);
push_up(o,l,r);
return ans;
}
int get(int o,int l,int r,int x,int y,int &val){
if(l>=x&&r<=y){
int ans=merge(o,l,r,val);
val=min(val,t[o]);
return ans;
}
pd(o,l,r);
int ans=0;
if(x<=mid)ans+=get(ls,l,mid,x,y,val);
if(y>mid)ans+=get(rs,mid+1,r,x,y,val);
push_up(o,l,r);
return ans;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",a+i);
build(1,1,n);
for(int i=1;i<=m;i++){
char x;
scanf(" %c",&x);
if(x=='c'){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
up(1,1,n,l,r,k);
}else{
int x;
scanf("%d",&x);
int d=getx(1,1,n,x);
if(x+1<=n)printf("%d\n",get(1,1,n,x+1,n,d));
else puts("0");
}
}
return 0;
}