比较简单的线段树的区间合并(比旅馆的稍微要考虑的东西要多一点)。
先放代码:
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 50010;
struct stuff{
int l,r; //左子,右子
int mm,rm,lm; //区间最大长度,区间左最大长度,区间右最大长度
}a[maxn<<2];
stack stk; //用来存爆点的栈
void up(int rt) //向上更新
{
a[rt].lm = a[rt<<1].lm;
a[rt].rm = a[rt<<1|1].rm;
a[rt].mm = max(a[rt<<1|1].lm+a[rt<<1].rm,max(a[rt<<1|1].mm,a[rt<<1].mm));
if(a[rt<<1].lm == a[rt<<1].r-a[rt<<1].l+1)
a[rt].lm+=a[rt<<1|1].lm;
if(a[rt<<1|1].rm == a[rt<<1|1].r-a[rt<<1|1].l+1)
a[rt].rm+=a[rt<<1].rm;
}
void build(int l,int r,int rt) //建树
{
a[rt].l=l;
a[rt].r=r;
if(l==r){
a[rt].lm=a[rt].rm=a[rt].mm = 1;
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
up(rt);
}
void update(int rt,int t,int X) //更新单点
{
if(a[rt].l==a[rt].r){
a[rt].mm=a[rt].lm=a[rt].rm = X;
return;
}
int m=(a[rt].l+a[rt].r)>>1;
if(t<=m)
update(rt<<1,t,X);
else
update(rt<<1|1,t,X);
up(rt);
}
int getn(int rt,int t) //查找
{
if(a[rt].l==a[rt].r || a[rt].mm==0 || a[rt].mm==a[rt].r-a[rt].l+1)
return a[rt].mm;
int m=(a[rt].l+a[rt].r)>>1;
//cout << m<=a[rt<<1].r - a[rt<<1].rm + 1)
return getn(rt<<1,t)+getn(rt<<1|1,m+1);
else
return getn(rt<<1,t);
}
else{
if(t<=a[rt<<1|1].l + a[rt<<1|1].lm - 1)
return getn(rt<<1|1,t)+getn(rt<<1,m);
else
return getn(rt<<1|1,t);
}
}
int main()
{
int n,m,cnt,num;
while(~scanf("%d%d",&n,&m)){
cnt=0;
build(1,n,1);
while(m--){
char c;
cin>>c;
if(c=='D'){
//cout << c <>num;
stk.push(num);
update(1,num,0);
}
else if(c=='Q'){
//cout << c<>num;
cout << getn(1,num) <
build、up、update这三个函数都是不变的,唯一需要多考虑的就是getn函数(下面拆开看):
if(a[rt].l==a[rt].r || a[rt].mm==0 || a[rt].mm==a[rt].r-a[rt].l+1)
return a[rt].mm;
这里当查找满足叶子节点、区间最大值=0、区间最大值=区间范围 时,就可以返回区间最大值了。(因为这里的返回不会对结果产生影响)。
if(t<=m){
if(t>=a[rt<<1].r - a[rt<<1].rm + 1)
return getn(rt<<1,t)+getn(rt<<1|1,m+1);
else
return getn(rt<<1,t);
}
这里我们拿向左走为例子:我们向左走,会有两种情况:
一是右边的区间是否为可取不会有影响(例如1~3这个子树,我们要看2这个点的最长长度,但是3这个点已经爆掉了,即2<3-0+1,所以就不用管4~∞了)。
二是右边区间会产生影响(即3没爆,2>3-3+1)。综上,我们在if(t<=m)内分了两种情况,向右走同理。