线段树本就擅长于处理区间问题,这题思考很久之后,我想到了做法,最开始整个区间是连续的,当破坏了一个村庄之后,当前区间的从左数和从右数的连续区间要更新,L数组用来存当前区间从左数最长区间,R数组用来存当前区间从右到左最长连续区间,S数组是当前区间最大连续区间,lc和rc是端点的区间。而这道题是单点更新,不需要标记,只要往上更新就行了
#include <iostream> #include <stdio.h> #include <string.h> #define siz 50005 #define lson(x) ((x)<<1) #define rson(x) (((x)<<1)|1) using namespace std; int sets[siz<<2],lc[siz<<2],rc[siz<<2]; int L[siz<<2],R[siz<<2],S[siz<<2]; int n,m; int sta[siz]; int length(int root){ return rc[root]-lc[root]+1; } void maintain(int root,int v){ L[root]=R[root]=S[root]=(v?length(root):0);///当前状态当前区间更新为有空房间和无空房间 } void pushup(int root){ S[root]=max(max(S[lson(root)],S[rson(root)]),R[lson(root)]+L[rson(root)]); L[root]=L[lson(root)]+(L[lson(root)]==length(lson(root))?L[rson(root)]:0); ///如果左儿子全是连续区间,则当前区间右儿子从左数的连续区间是从左边数的连续区间的一部分 R[root]=R[rson(root)]+(R[rson(root)]==length(rson(root))?R[lson(root)]:0); ///同理 } void build(int root,int l,int r){ lc[root]=l; rc[root]=r; if(l==r){ maintain(root,1);///建树,1代表整个区间连续 return ; } int mid=(lc[root]+rc[root])>>1; build(lson(root),l,mid); build(rson(root),mid+1,r); pushup(root); } void modify(int u,int k,int v){///类似于普通线段树的单点更新 //cout<<u<<" "<<k<<"^^^"<<lc[u]<<endl; if(lc[u]==rc[u]&&lc[u]==k){ maintain(u,v); return ; } int mid=(lc[u]+rc[u])>>1; //cout<<mid<<" "<<lc[u]<<" "<<rc[u]<<endl; if(k<=mid) modify(lson(u),k,v); else modify(rson(u),k,v); pushup(u); } int query(int rt,int k){ if(lc[rt]==rc[rt]&&lc[rt]==k){ return S[rt]; } int mid=(lc[rt]+rc[rt])>>1; if(k<=mid){ if(mid-R[lson(rt)]+1<=k){///最左边比k小,说明k在从右数连续区间内 return R[lson(rt)]+L[rson(rt)];///加上左边的区间 }else{ return query(lson(rt),k); } }else{ if(mid+1+L[rson(rt)]-1>=k){///同理 return L[rson(rt)]+R[lson(rt)]; }else{ return query(rson(rt),k); } } } int main() { while(~scanf("%d%d",&n,&m)){ build(1,1,n); int ind=0; while(m--){ //getchar(); char c[2]; int v; scanf("%s",c); if(c[0]=='D'){ scanf("%d",&v); sta[ind++]=v; modify(1,v,0);///村庄被销毁,断点 } else if(c[0]=='Q'){ scanf("%d",&v); int ans=query(1,v); printf("%d\n",ans); } else{ if(ind>0){ v=sta[--ind]; modify(1,v,1);///1修复村庄 } } // cout<<1<<endl; } } return 0; }