题意:一条线上有n个点,D x是破坏这个点,Q x是表示查询x所在的最长的连续的点的个数,R是恢复上一次破坏的点。
思路:这题的关键是查询。
将被毁的村庄看成空位,当查询某个点的时候,如果我们知道它左边最近的空位a和右边最近的空位b,
那么我们只要查询区间[a,b]中的个数,即为答案,因为[a,b]之间不可能有空位存在了。
那么如何获取这样的a和b呢,这个就和HDU 4302 Holedox Eating 差不多了。
对每个节点,存储该区间中 空位的最大位置 和 空位的最小位置,还有 该区间村庄的个数。
这样当我们查询某点x的时候,那么我们只要查询[1,x]区间最大的空位编号a,[x,n]区间最小的空位编号为b,
那么a~b之间就没别的空位,查询[a,b]区间的村庄个数即为答案。
HDU 1540 比POJ 2892 要坑,如果在HDU 1540 要注意一下几点。
如下:
(1)多case
(2)某个村庄可以被毁坏多次(必须全部入栈),但只需要一次就能将其恢复(见下面的这组数据)
(3)D 3 D 2 D 1 D 1 D 2
R 恢复2
R 恢复1
R 恢复3
#include <iostream> #include <stdio.h> #include <set> #include <string.h> #define lson rt<<1,L,mid #define rson rt<<1|1,mid+1,R using namespace std; const int maxn=50005; const int INF=0X3f3f3f3f; int n,m; int vis[maxn]; //vis[i]=1表示该村庄被毁 int last[maxn]; //栈存取被毁村庄的编号 int idx=-1; struct Node { int sum; //该区间中村庄的个数 int maxval; //该区间中被毁村庄的最大编号 int minval; //该区间中被毁村庄的最小编号 } tree[maxn<<2]; void build(int rt,int L,int R) { tree[rt].sum=R-L+1; tree[rt].maxval=-INF; tree[rt].minval=INF; if(L==R) return; int mid=(L+R)>>1; build(lson); build(rson); } void pushUp(int rt) { tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum; tree[rt].maxval=max(tree[rt<<1].maxval,tree[rt<<1|1].maxval); tree[rt].minval=min(tree[rt<<1].minval,tree[rt<<1|1].minval); } void update(int rt,int L,int R,int x,int c) { if(L==R) { tree[rt].sum=c; //c=1表示恢复村庄,c=0表示摧毁该村庄 if(c) { tree[rt].minval=INF; tree[rt].maxval=-INF; } else { tree[rt].maxval=L; tree[rt].minval=L; } return; } int mid=(L+R)>>1; if(x<=mid) update(lson,x,c); else update(rson,x,c); pushUp(rt); } //查询左区间被毁村庄的最大编号 int queryMax(int rt,int L,int R,int l,int r) { if(l<=L&&R<=r) { return tree[rt].maxval; } int ret; int mid=(L+R)>>1; if(r<=mid) ret=queryMax(lson,l,r); else if(l>mid) ret=queryMax(rson,l,r); else { ret=max(queryMax(lson,l,mid),queryMax(rson,mid+1,r)); } return ret; } //查询右区间被毁村庄的最小编号 int queryMin(int rt,int L,int R,int l,int r) { if(l<=L&&R<=r) { return tree[rt].minval; } int ret; int mid=(L+R)>>1; if(r<=mid) ret=queryMin(lson,l,r); else if(l>mid) ret=queryMin(rson,l,r); else { ret=min(queryMin(lson,l,mid),queryMin(rson,mid+1,r)); } return ret; } //查询该区间的村庄个数 int querySum(int rt,int L,int R,int l,int r) { int ret=0; if(l<=L&&R<=r) { return tree[rt].sum; } int mid=(L+R)>>1; if(l<=mid) ret+=querySum(lson,l,r); if(r>mid) ret+=querySum(rson,l,r); return ret; } int main() { memset(vis,0,sizeof(vis)); char str[5]; int x,y,a; while(scanf("%d%d",&n,&m)!=EOF) { memset(vis,0,sizeof(vis)); idx=-1; build(1,1,n); for(int i=1; i<=m; i++) { scanf("%s",str); if(str[0]=='D') { scanf("%d",&a); update(1,1,n,a,0); last[++idx]=a; //不管之前是否被毁,都加入到栈 vis[a]=1; } else if(str[0]=='R') { /* 可能有的已经之前被修复了,所以这里要用while先判断一下 */ while(vis[last[idx]]==0 &&idx>=0) idx--; if(idx==-1) continue; //如果没有被毁的,则直接忽视就行 update(1,1,n,last[idx],1); vis[last[idx]]=0; idx--; } else { scanf("%d",&a); if(vis[a]==1) printf("0 "); else { //x为a左边距离a最近的空位(即被拆的村庄) //y为a右边距离a最近的空位(即被拆的村庄) x=queryMax(1,1,n,1,a); y=queryMin(1,1,n,a,n); //printf("%d,%d ",x,y); printf("%d ",querySum(1,1,n,x,y)); //因为x、y之间没有被毁的村庄,所以查询x、y之间的村庄个数即可 } } } } return 0; }
还有另一个做法,那就是每个节点记录的是:左端开始连续的个数,右端开始连续的个数,最大的连续个数。
当查询某点x的时候,查询[1,x-1]区间,合并成t1节点;查询[x+1,n],合并成t2节点,答案即为t1.rsum+1+t2.lsum。
#include <iostream> #include <stdio.h> #include <set> #include <string.h> #define lson rt<<1,L,mid #define rson rt<<1|1,mid+1,R using namespace std; const int maxn=50005; const int INF=0X3f3f3f3f; int n,m; int vis[maxn]; //vis[i]=1表示该村庄被毁,=0表示村庄没被毁 int last[maxn]; //栈存取被毁村庄的编号 int idx=-1; struct Node { int lsum,msum,rsum; //左连续的最大值,连续的最大值,右连续的最大值 int len; } tree[maxn<<2]; void build(int rt,int L,int R) { tree[rt].len=tree[rt].lsum=tree[rt].rsum=tree[rt].msum=R-L+1; if(L==R) return; int mid=(L+R)>>1; build(lson); build(rson); } void pushUp(Node &rt,Node &ls,Node &rs){ rt.lsum=ls.lsum; rt.rsum=rs.rsum; if(ls.lsum==ls.len) rt.lsum+=rs.lsum; if(rs.rsum==rs.len) rt.rsum+=ls.rsum; rt.msum=ls.rsum+rs.lsum; rt.msum=max(rt.msum,max(ls.msum,rs.msum)); } void update(int rt,int L,int R,int x,int c) { if(L==R) { //c=1表示恢复村庄,c=0表示摧毁该村庄 if(c) { tree[rt].lsum=tree[rt].rsum=tree[rt].msum=1; } else { tree[rt].lsum=tree[rt].rsum=tree[rt].msum=0; } return; } int mid=(L+R)>>1; if(x<=mid) update(lson,x,c); else update(rson,x,c); pushUp(tree[rt],tree[rt<<1],tree[rt<<1|1]); } //将对应区间[l,r]合并成一个节点返回 Node query(int rt,int L,int R,int l,int r){ if(l<=L&&R<=r){ return tree[rt]; } int mid=(L+R)>>1; int length=(R-L+1); Node tmp,r1,r2; if(r<=mid) tmp=query(lson,l,r); else if(l>mid) tmp=query(rson,l,r); else{ r1=query(lson,l,mid); r2=query(rson,mid+1,r); tmp.len=r1.len+r2.len; pushUp(tmp,r1,r2); } return tmp; } int main() { memset(vis,0,sizeof(vis)); Node t1,t2; char str[5]; int a,ans; while(scanf("%d%d",&n,&m)!=EOF) { memset(vis,0,sizeof(vis)); idx=-1; build(1,1,n); for(int i=1; i<=m; i++) { scanf("%s",str); if(str[0]=='D') { scanf("%d",&a); update(1,1,n,a,0); last[++idx]=a; //不管之前是否被毁,都加入到栈 vis[a]=1; } else if(str[0]=='R') { /* 可能有的已经之前被修复了,所以这里要用while先判断一下 */ while(vis[last[idx]]==0 &&idx>=0) idx--; if(idx==-1) continue; //如果没有被毁的,则直接忽视就行 update(1,1,n,last[idx],1); vis[last[idx]]=0; idx--; } else { scanf("%d",&a); if(vis[a]==1) printf("0 "); else { //为方便起见,1和n单独分出来 if(a==1){ t1=query(1,1,n,a+1,n); ans=t1.lsum+1; } else if(a==n){ t1=query(1,1,n,1,a-1); ans=t1.rsum+1; } else{ t1=query(1,1,n,1,a-1); t2=query(1,1,n,a+1,n); ans=t1.rsum+1+t2.lsum; } printf("%d ",ans); } } } } return 0; }