1. 程式人生 > >【資料結構】【線段樹】2018國慶三校聯考D5T3

【資料結構】【線段樹】2018國慶三校聯考D5T3

題意:

在這裡插入圖片描述


分析:

有一個顯然的暴力方法:
對每個詢問,從左往右做一次,記錄字首和,當某個位置字首和為負後,則刪去當前點。再從右往左做一次。

考慮使這個過程變得高效:
可以將詢問按左端點從右往左排序,然後用棧依次處理每個在從左往右考慮時是否需要刪除。
再利用線段樹,求出沒有刪除的部分的最小字尾和,其相反數(和0取min)即為從右往左考慮時需要刪除的個數。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf #define MAXN 500010 #define INF 0x3FFFFFFF using namespace std; typedef pair<int,int> pii; struct node{ int l,r,id; bool operator < (const node &a) const{ if(l!=a.l) return l<a.l; return r<a.r; } }q[MAXN]; int tree[MAXN*4],sum[MAXN*4],ans[MAXN]; int st[MAXN]
,top,n; char s[MAXN]; void change(int pos,int val,int l=1,int r=n,int id=1){ if(l==r){ tree[id]=val; sum[id]=val; return ; } int mid=(l+r)>>1; if(pos<=mid) change(pos,val,l,mid,id<<1); else change(pos,val,mid+1,r,id<<1|1); tree[id]=min(tree[id<<1]+sum[id<<
1|1],tree[id<<1|1]); sum[id]=sum[id<<1]+sum[id<<1|1]; } pii que(int l1,int r1,int l=1,int r=n,int id=1){ if(l>=l1&&r<=r1) return make_pair(tree[id],sum[id]); int mid=(l+r)>>1; int res=INF; pii tmp1=make_pair(INF,0),tmp2=make_pair(INF,0); if(r1>mid){ tmp1=que(l1,r1,mid+1,r,id<<1|1); res=min(res,tmp1.first); } if(l1<=mid){ tmp2=que(l1,r1,l,mid,id<<1); res=min(res,tmp2.first+tmp1.second); } return make_pair(res,tmp1.second+tmp2.second); } int bque(int l,int r,int val){ int res=top+1; while(l<=r){ int mid=(l+r)>>1; if(st[mid]<=val){ r=mid-1; res=mid; } else l=mid+1; } return top-res+1; } int main(){ freopen("sworder.in","r",stdin); freopen("sworder.out","w",stdout); int Q; SF("%d",&n); SF("%s",s+1); SF("%d",&Q); for(int i=1;i<=Q;i++){ SF("%d%d",&q[i].l,&q[i].r); q[i].id=i; } sort(q+1,q+1+Q); int las=n; for(int i=Q;i>=1;i--){ while(las>=q[i].l){ if(s[las]=='C'){ st[++top]=las; } else{ if(top!=0) change(st[top],-1); change(las,1); top=max(top-1,0); } las--; } ans[q[i].id]=bque(1,top+1,q[i].r)-min(0,(que(q[i].l,q[i].r)).first); } for(int i=1;i<=Q;i++) PF("%d\n",ans[i]); } /* 11 VVVCCCCCCVV 3 1 11 4 9 1 6 */