bzoj 4592: [Shoi2015]脑洞治疗仪 线段树

Description

曾经发明了自动刷题机的发明家SHTSC又公开了他的新发明:脑洞治疗仪–一种可以治疗他因为发明而日益增大的脑洞的神秘装置。
为了简单起见,我们将大脑视作一个01序列。1代表这个位置的脑组织正常工作,0代表这是一块脑洞。
1 0 1 0 0 0 1 1 1 0
脑洞治疗仪修补某一块脑洞的基本工作原理就是将另一块连续区域挖出,将其中正常工作的脑组织填补在这块脑洞中。
(所以脑洞治疗仪是脑洞的治疗仪?)
例如,用上面第8号位置到第10号位置去修补第1号位置到第4号位置的脑洞。我们就会得到:
1 1 1 1 0 0 1 0 0 0
如果再用第1号位置到第4号位置去修补第8号位置到第10号位置:
0 0 0 0 0 0 1 1 1 1
这是因为脑洞治疗仪会把多余出来的脑组织直接扔掉。
如果再用第7号位置到第10号位置去填补第1号位置到第6号位置:
1 1 1 1 0 0 0 0 0 0
这是因为如果新脑洞挖出来的脑组织不够多,脑洞治疗仪仅会尽量填补位置比较靠前的脑洞。
假定初始时SHTSC并没有脑洞,给出一些挖脑洞和脑洞治疗的操作序列,你需要即时回答SHTSC的问题:
在大脑某个区间中最大的连续脑洞区域有多大。
Input

第一行两个整数n,m。表示SHTSC的大脑可分为从1到n编号的n个连续区域。有m个操作。
以下m行每行是下列三种格式之一。
0 l r :SHTSC挖了一个从l到r的脑洞。
1 l0 r0 l1 r1:SHTSC进行了一次脑洞治疗,用从l0到r0的脑组织修补l1到r1的脑洞。
2 l r :SHTSC询问l到r这段区间最大的脑洞有多大。
n,m <=200000,1<=l<=r<=n
Output

对于每个询问,输出一行一个整数,表示询问区间内最大连续脑洞区域有多大。

Sample Input
10 10
0 2 2
0 4 6
0 10 10
2 1 10
1 8 10 1 4
2 1 10
1 1 4 8 10
2 1 10
1 7 10 1 6
2 1 10

Sample Output
3
3
6
6

分析:
对每个点维护 s u m , l a n s , r a n s , a n s sum,lans,rans,ans sum,lans,rans,ans表示区间 0 0 0个数,左端连续 0 0 0个数,右端连续 0 0 0个数,答案。
操作1直接修改打lazy。
操作2先算出 [ 1 , l 1 ] [1,l1] [1,l1]中0的个数再加上 [ l 0 , r 0 ] [l0,r0] [l0,r0] 1 1 1的个数。然后就可以向求第 k k k大的方法求出应该覆盖到那个位置即可。
操作3直接线段树上求解即可。

代码:

/**************************************************************
    Problem: 4592
    User: ypxrain
    Language: C++
    Result: Accepted
    Time:10452 ms
    Memory:20044 kb
****************************************************************/
 
#include <iostream>
#include <cstdio>
#include <cmath>
 
const int maxn=2e5+7;
 
using namespace std;
 
int n,m,op,x,y,l,r;
 
struct rec{
    int sum,lans,rans,ans,len;
};
 
struct node{
    rec data;
    int lazy;
}t[maxn*4];
 
rec merge(rec a,rec b)
{
    rec c;
    c.ans=max(a.ans,b.ans);
    c.ans=max(c.ans,a.rans+b.lans);
    c.len=a.len+b.len;
    c.sum=a.sum+b.sum;
    if (a.lans==a.len) c.lans=a.lans+b.lans;
                  else c.lans=a.lans;
    if (b.rans==b.len) c.rans=b.rans+a.rans;
                  else c.rans=b.rans;
    return c;
}
 
void clean(int p,int l,int r)
{
    if (t[p].lazy==-1) return;
    int mid=(l+r)/2;
    if (t[p].lazy==1)
    {
        t[p*2].lazy=t[p*2+1].lazy=t[p].lazy;
        t[p*2].data=(rec){0,0,0,0,mid-l+1};
        t[p*2+1].data=(rec){0,0,0,0,r-mid};
    }
    else
    {
        t[p*2].lazy=t[p*2+1].lazy=t[p].lazy;
        t[p*2].data=(rec){mid-l+1,mid-l+1,mid-l+1,mid-l+1,mid-l+1};
        t[p*2+1].data=(rec){r-mid,r-mid,r-mid,r-mid,r-mid};
    }
    t[p].lazy=-1;
}
 
void build(int p,int l,int r)
{
    t[p].data.len=r-l+1;
    t[p].lazy=-1;
    if (l==r) return;
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
}
 
void ins(int p,int l,int r,int x,int y,int k)
{
    if (x>y) return;
    if ((l==x) && (r==y))
    {
        t[p].lazy=k;
        if (k==1) t[p].data=(rec){0,0,0,0,r-l+1};
             else t[p].data=(rec){r-l+1,r-l+1,r-l+1,r-l+1,r-l+1};
        return;
    }
    clean(p,l,r);
    int mid=(l+r)/2;
    if (y<=mid) ins(p*2,l,mid,x,y,k);
    else if (x>mid) ins(p*2+1,mid+1,r,x,y,k);
    else
    {
        ins(p*2,l,mid,x,mid,k);
        ins(p*2+1,mid+1,r,mid+1,y,k);
    }
    t[p].data=merge(t[p*2].data,t[p*2+1].data);
}
 
rec query(int p,int l,int r,int x,int y)
{
    if ((l==x) && (r==y)) return t[p].data;
    clean(p,l,r);
    int mid=(l+r)/2;
    if (y<=mid) return query(p*2,l,mid,x,y);
    else if (x>mid) return query(p*2+1,mid+1,r,x,y);
    else return merge(query(p*2,l,mid,x,mid),query(p*2+1,mid+1,r,mid+1,y));
}
 
int find(int p,int l,int r,int k)
{
    if (l==r) return l;
    clean(p,l,r);
    int mid=(l+r)/2;
    if (t[p*2].data.sum<k) return find(p*2+1,mid+1,r,k-t[p*2].data.sum);
                      else return find(p*2,l,mid,k);
}
 
int main()
{
    scanf("%d%d",&n,&m);
    build(1,1,n);       
    ins(1,1,n,1,n,1);
    for (int i=1;i<=n;i++)
    {       
        scanf("%d",&op);
        if (op==0)
        {
            scanf("%d%d",&x,&y);
            ins(1,1,n,x,y,0);
        }
        if (op==1)
        {
            scanf("%d%d%d%d",&x,&y,&l,&r);
            int s1=y-x+1-query(1,1,n,x,y).sum;
            ins(1,1,n,x,y,0);
            int s0;
            if (l>1) s0=query(1,1,n,1,l-1).sum;
                else s0=0;
            int p=find(1,1,n,min(s0+s1,query(1,1,n,1,r).sum));
            if (min(s0+s1,query(1,1,n,1,r).sum)) ins(1,1,n,l,p,1);
        }
        if (op==2)
        {
            scanf("%d%d",&x,&y);
            rec d=query(1,1,n,x,y);
            printf("%d\n",d.ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值