传送门
十倍经验题= =
线段树的一个结点维护3个值,分别是区间最长,左端点连续最长,右端点连续最长,然后就可以搞了。
update的时候注意分几种情况,还有整个区间都被覆盖的特殊情况。
最后查询开头的时候需要一点小技巧。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_n=5e4+5;
const int max_tree=max_n*4;
struct hp{
int val,lval,rval;
int l,r,len;
};
hp tree[max_tree];
int delta[max_tree];
int n,m,len,lrange,rrange,opt;
int ans;
inline void update(int now){
if (tree[now<<1].lval==tree[now<<1].len)
tree[now].lval=tree[now<<1].lval+tree[now<<1|1].lval;
else
tree[now].lval=tree[now<<1].lval;
if (tree[now<<1|1].rval==tree[now<<1|1].len)
tree[now].rval=tree[now<<1|1].rval+tree[now<<1].rval;
else
tree[now].rval=tree[now<<1|1].rval;
int midval=tree[now<<1].rval+tree[now<<1|1].lval;
tree[now].val=max(midval,max(tree[now<<1].val,tree[now<<1|1].val));
}
inline void pushdown(int now,int l,int r,int mid){
if (delta[now]>=0){
if (delta[now]==0){
tree[now<<1].val = tree[now<<1].len;
tree[now<<1].lval = tree[now<<1].len;
tree[now<<1].rval = tree[now<<1].len;
delta[now<<1] = delta[now];
tree[now<<1|1].val = tree[now<<1|1].len;
tree[now<<1|1].lval = tree[now<<1|1].len;
tree[now<<1|1].rval = tree[now<<1|1].len;
delta[now<<1|1] = delta[now];
}
else{
tree[now<<1].val = 0;
tree[now<<1].lval = 0;
tree[now<<1].rval = 0;
delta[now<<1] = delta[now];
tree[now<<1|1].val = 0;
tree[now<<1|1].lval = 0;
tree[now<<1|1].rval = 0;
delta[now<<1|1] = delta[now];
}
delta[now]=-1;
}
}
inline void build(int now,int l,int r){
int mid=(l+r)>>1;
tree[now].l=l; tree[now].r=r;
tree[now].len=r-l+1;
delta[now]=-1;
if (l==r){
tree[now].val = tree[now].len;
tree[now].lval = tree[now].len;
tree[now].rval = tree[now].len;
return;
}
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
inline void interval_change(int now,int l,int r,int lrange,int rrange,int v){
int mid=(l+r)>>1;
if (lrange<=l&&r<=rrange){
delta[now]=v;
if (v==0){
tree[now].val = tree[now].len;
tree[now].lval = tree[now].len;
tree[now].rval = tree[now].len;
}
else{
tree[now].val = 0;
tree[now].lval = 0;
tree[now].rval = 0;
}
return;
}
pushdown(now,l,r,mid);
if (lrange<=mid)
interval_change(now<<1,l,mid,lrange,rrange,v);
if (mid+1<=rrange)
interval_change(now<<1|1,mid+1,r,lrange,rrange,v);
update(now);
}
inline int find(int now,int l,int r,int len){
int mid=(l+r)>>1;
if (l==r) return l;
pushdown(now,l,r,mid);
if (tree[now<<1].val>=len)
return find(now<<1,l,mid,len);
else
if (tree[now<<1].rval+tree[now<<1|1].lval>=len)
return mid-tree[now<<1].rval+1;
else
return find(now<<1|1,mid+1,r,len);
}
int main(){
scanf("%d%d",&n,&m);
build(1,1,n);
for (int i=1;i<=m;++i){
scanf("%d",&opt);
if (opt==1){
scanf("%d",&len);
if (tree[1].val>=len){
ans=find(1,1,n,len);
printf("%d\n",ans);
lrange=ans;
rrange=ans+len-1;
interval_change(1,1,n,lrange,rrange,1);
}
else
printf("0\n");
}
else{
scanf("%d%d",&lrange,&len);
rrange=lrange+len-1;
interval_change(1,1,n,lrange,rrange,0);
}
}
}
①如此傻逼的一道题竟然让我写成了140+,心里很不爽。最近写数据结构似乎非常冗长,调试慢,手残错误多。以后应该注意尽量避免。
②刚开始的思路错了,把区间最长的开头记了。其实是不对的,因为有可能有的区间不是最长的,但是也满足要求,同时起点靠前。
③后来又发现问题:update。呵呵傻逼了。
Loli的测试碰巧考到了这道题,结果我因为奇怪的崩溃被卡成了70分。也不能说是奇怪,确实是有自己傻逼的地方。
惨痛的教训告诉我:
1、一提到线段树,一定要警醒三点:update、pushdown、递归到底层。所有的操作之前都要pushdown,所有的修改之后都要update,每个函数都要考虑递归到最底层的情况。
2、开Warning