这个题目好像不是很难,可是我想了很久,还超时了。。我用的是普通线段树和二分,第一次用二分超时啊啊啊啊啊
好,让我们言归正传。(参考别的大佬的做法,贼6)
看到这个题目之后,能够想到它是为了求包含一个点的最大连续区间。那么多的区间,应该是要用线段树来做。
可以想得到,查询的时候,是从这个点展开,判断左右是否连续。
首先,我们用递归查询到这个点,或者说是包含这个点的连续区间,假设它是一个左子树,那我们就要加上它相邻的右子树的从左边开始的连续区间,如果它是右子树,那么就按相反。
这样的话,我们不如就建立两个数组,一个是从左边开始累加区间长度的数组(lsum[]),一个是从右边开始累加区间长度的数组(rsum[]),然后建树,更新节点,查询。
还要提一点,由于题目中有一个什么重建,emmm,就是先进后出的栈嘛,所以我们这里就可以用到c++中的一个stack容器。
有这几个常用的操作吧:
stack
1.入栈:如s.push(x);
2.出栈:如 s.pop().注意:出栈操作只是删除栈顶的元素,并不返回该元素。
3.访问栈顶:如s.top();
4.判断栈空:如s.empty().当栈空时返回true。
5.访问栈中的元素个数,如s.size();
贴代码:
#include"cstdio"
#include"stack"
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 50010
int lsum[4*maxn],rsum[4*maxn],flag;
void Build(int l,int r,int rt) {
lsum[rt] = rsum[rt] = r-l+1;
if (l == r) return;
int m = (l+r) >> 1;
Build(lson);
Build(rson);
}
void Pushup(int rt,int m) {
lsum[rt] = lsum[rt<<1];
rsum[rt] = rsum[rt<<1|1];
if (lsum[rt] == m - (m>>1)) lsum[rt] += lsum[rt<<1|1];//yi cuo
if (rsum[rt] == m>>1) rsum[rt] += rsum[rt<<1];
}
void Updata(int p,int x,int l,int r,int rt) {
if (l == r) {
lsum[rt] = rsum[rt] = x;
return;
}
int m = (l+r) >> 1;
if (p <= m) Updata(p,x,lson); else Updata(p,x,rson);
Pushup(rt,r-l+1);
}
int Query(int p,int l,int r,int rt) {
if (rsum[rt] >= r-p+1) {
flag = 1;
return rsum[rt];
}
if (lsum[rt] >= p-l+1) {
flag = 1;
return lsum[rt];
}
if (l == r) return 0;
int m = (l+r) >> 1, t = 0;
if (p > m) {
t = Query(p,rson);
if (flag) {
flag = 0;
t += rsum[rt<<1];
}
} else {
t = Query(p,lson);
if (flag) {
flag = 0;
t += lsum[rt<<1|1];
}
}
return t;
}
int main() {
int n,m;
// freopen("1.in","r",stdin);
while(~scanf("%d%d",&n,&m)) {
Build(1,n,1);
char cmd[2];
stack sta;
for (int i = 0;i < m;i ++) {
scanf("%s",cmd);
if (cmd[0] == 'D') {
int x;
scanf("%d",&x);
sta.push(x);
Updata(sta.top(),0,1,n,1);
} else
if (cmd[0] == 'Q') {
int x;
flag = 0;
scanf("%d",&x);
printf("%d\n",Query(x,1,n,1));
} else {
Updata(sta.top(),1,1,n,1);
sta.pop();
}
}
}
return 0;
}