4592: [Shoi2015]脑洞治疗仪

4592: [Shoi2015]脑洞治疗仪

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 338   Solved: 142
[ Submit][ Status][ Discuss]

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 r2 :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

HINT

Source

[ Submit][ Status][ Discuss]

考虑使用线段树维护治疗情况
对于线段树内每个节点,维护一下信息
cnt[o][i]:区间o里数字i出现次数
Left[o]:区间o左起最长连续0长度
Right[o]:同上
Max[o]:区间o内最长连续0长度

对于操作0,等价于区间覆盖,很好维护
对于操作1,先统计[l0,r0]内1的个数,然后区间覆盖,对于[l1,r1],只需要在线段树上二分在当前1的个数情况下最右边能覆盖到那个位置,那就又转换成区间覆盖了
对于操作2,把O(logn)个区间拿出来简单组合一下即可
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
 
const int maxn = 2E5 + 20;
const int T = 4;
 
int n,m,tp,Ans,cnt[maxn*T][2],Max[maxn*T],Left[maxn*T],Right[maxn*T],Mark[maxn*T],stk[30];
bool All[maxn*T];
 
void Build(int o,int l,int r)
{
    Mark[o] = -1; cnt[o][1] = r - l + 1;
    if (l == r) return; int mid = l + r >> 1;
    Build(o<<1,l,mid); Build(o<<1|1,mid+1,r);
}
 
void pushdown(int o,int l,int r)
{
    if (Mark[o] == -1) return;
    if (Mark[o])
    {
        cnt[o][1] = r - l + 1; All[o] = 0;
        cnt[o][0] = Left[o] = Right[o] = Max[o] = 0;
    }
    else
    {
        cnt[o][1] = 0; All[o] = 1;
        cnt[o][0] = Left[o] = Right[o] = Max[o] = r - l + 1;
    }
    if (l == r) {Mark[o] = -1; return;}
    Mark[o<<1] = Mark[o<<1|1] = Mark[o]; Mark[o] = -1;
}
 
void maintain(int o)
{
    int lc = (o<<1),rc = (o<<1|1);
    cnt[o][0] = cnt[lc][0] + cnt[rc][0];
    cnt[o][1] = cnt[lc][1] + cnt[rc][1];
    All[o] = All[lc] && All[rc] ? 1 : 0;
    Left[o] = All[lc] ? Max[lc] + Left[rc] : Left[lc];
    Right[o] = All[rc] ? Max[rc] + Right[lc] : Right[rc];
    Max[o] = max(Max[lc],Max[rc]); Max[o] = max(Max[o],Right[lc] + Left[rc]);
}
 
int Cover_0(int o,int l,int r,int ql,int qr)
{
    pushdown(o,l,r);
    if (ql <= l && r <= qr)
    {
        int ret = cnt[o][1];
        Mark[o] = 0; pushdown(o,l,r);
        return ret;
    }
    int ret = 0,mid = l + r >> 1;
    if (ql <= mid) ret = Cover_0(o<<1,l,mid,ql,qr); else pushdown(o<<1,l,mid);
    if (qr > mid) ret += Cover_0(o<<1|1,mid+1,r,ql,qr); else pushdown(o<<1|1,mid+1,r);
    maintain(o); return ret;
}
 
void Cover_1(int o,int l,int r,int ql,int qr,int &res)
{
    int mid = l + r >> 1; pushdown(o,l,r);
    if (ql <= l && r <= qr)
    {
        if (cnt[o][0] <= res)
        {
            res -= cnt[o][0]; Mark[o] = 1;
            pushdown(o,l,r); return;
        }
        int mid = l + r >> 1;
        if (res) Cover_1(o<<1,l,mid,ql,qr,res); else pushdown(o<<1,l,mid);
        if (res) Cover_1(o<<1|1,mid+1,r,ql,qr,res); else pushdown(o<<1|1,mid+1,r);
        maintain(o);
    }   
    if (ql <= mid && res) Cover_1(o<<1,l,mid,ql,qr,res); else pushdown(o<<1,l,mid);
    if (qr > mid && res) Cover_1(o<<1|1,mid+1,r,ql,qr,res); else pushdown(o<<1|1,mid+1,r);
    maintain(o);
}
 
void GetRange(int o,int l,int r,int ql,int qr)
{
    pushdown(o,l,r);
    if (ql <= l && r <= qr)
        {Ans = max(Ans,Max[o]); stk[++tp] = o; return;}
    int mid = l + r >> 1;
    if (ql <= mid) GetRange(o<<1,l,mid,ql,qr);
    if (qr > mid) GetRange(o<<1|1,mid+1,r,ql,qr);
}
 
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    n = getint(); m = getint(); Build(1,1,n);
    while (m--)
    {
        int typ = getint(),l,r;
        l = getint(); r = getint();
        if (!typ) Cover_0(1,1,n,l,r);
        else if (typ == 1)
        {
            int ret = Cover_0(1,1,n,l,r);
            l = getint(); r = getint();
            if (ret) Cover_1(1,1,n,l,r,ret);
        }
        else
        {
            tp = Ans = 0;
            GetRange(1,1,n,l,r); stk[tp + 1] = 0;
            for (int i = 1; i <= tp; i++)
            {
                Ans = max(Ans,Right[stk[i - 1]] + Left[stk[i]]);
                if (!All[stk[i]] || All[stk[i - 1]]) continue;
                int now = Right[stk[i - 1]];
                for (int j = i; j <= tp; j++)
                    if (All[stk[j]]) now += Max[stk[j]];
                    else {now += Left[stk[j]]; break;}
                Ans = max(Ans,now);
            }
            printf("%d\n",Ans);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值