线段树重开poj - 3667

本题目重做感受颇多,

思考的深度决定了对线段树的掌握程度,只有自己创生的才是自己的啊。

本题用到了三个维护值,sum表示区间最长连续区间,lsum为区间左面最长连续区间长。rsum同理。

现在来说明sum为什么具有 可维护性,

线段树的可维护性是相对于更新区间而言的,因为更新的logn个区间总是被刷成全空房间,或全满房间,所以这几个区间的三个属性都可以得到维护,并且具有向上的合并性质

所以可以维护,一旦可以维护,那就啥也不说了。

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define rep(i,n) for(int (i)=0;(i)<(n);i++)
#define Rep(i,n) for(int (i)=1;(i)<=(n);i++)

const int N = 51111;
int sum[N<<2],col[N<<2],lsum[N<<2],rsum[N<<2];
void push_up(int l,int r,int rt){
   sum[rt] = max(sum[rt<<1],sum[rt<<1|1]);
   sum[rt] = max(sum[rt],rsum[rt<<1]+lsum[rt<<1|1]);
   int v = lsum[rt<<1];
   int m = (l+r)>>1;
   lsum[rt] = (v==m-l+1 ? v+lsum[rt<<1|1]:v);
   v = rsum[rt<<1|1];
   rsum[rt] = (v==r-m ? v+rsum[rt<<1]:v);
}
void upd_one_seg(int l,int r,int rt,int f){
   if(!f){
      sum[rt] = lsum[rt] = rsum[rt] = r-l+1;
   }
   else {
      sum[rt] = lsum[rt] = rsum[rt] = 0;
   }
}
void push_down(int l,int r,int rt){
  if(col[rt]!=-1){
     int m = (l+r)>>1;
     upd_one_seg(lson,col[rt]);
     upd_one_seg(rson,col[rt]);
     col[rt<<1] = col[rt<<1|1] = col[rt];
     col[rt] = -1;
  }
}
void build(int l,int r,int rt){
  col[rt] = -1;
  if(l==r){
     sum[rt] = lsum[rt] = rsum[rt] = 1;
     return ;
  }
  int m = (l+r)>>1;
  build(lson);
  build(rson);
  push_up(l,r,rt);
}
void update(int l,int r,int rt,int L,int R,int f){
  if(L<=l&&r<=R){
    col[rt] = f;
    upd_one_seg(l,r,rt,f);
    return ;
  }
  push_down(l,r,rt);
  int m = (l+r)>>1;
  if(L<=m) update(lson,L,R,f);
  if(R>m) update(rson,L,R,f);
  push_up(l,r,rt);
}
int query(int l,int r,int rt,int len){
  if(l==r){
     if(sum[rt] == len) return l;
     return 0;
  }
  push_down(l,r,rt);
  int m = (l+r)>>1;
  if(sum[rt<<1]>=len) return query(lson,len);
  int mid = rsum[rt<<1]+lsum[rt<<1|1];
  if(mid >= len){return m-rsum[rt<<1]+1;}
  else if(sum[rt<<1|1]>=len) return query(rson,len);
  return 0;
}
int n,m;
int main()
{
   while(scanf("%d %d",&n,&m)==2){
     build(1,n,1);
     while(m--){
        int cmd,x,y;
        scanf("%d",&cmd);
        if(cmd == 1){
             scanf("%d",&x);
             int p = query(1,n,1,x);
             printf("%d\n",p);
             if(p){
                update(1,n,1,p,p+x-1,1);
             }
        }
        else {
             scanf("%d %d",&x,&y);
             update(1,n,1,x,x+y-1,0);
        }
     }
   }
   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值