【BZOJ-4592】脑洞治疗仪 线段树

4592: [Shoi2015]脑洞治疗仪

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 69  Solved: 38
[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

By 佚名上传

Solution

线段树维护区间连续的东西....

维护一下左右端点合并的时候注意一下就可以,对于填脑洞操作,进行二分,即可

Code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define maxn 210000
int n,m;
struct TreeNode{int l,r,size,len,lx,rx,tag,ans;}tree[maxn<<2];
TreeNode Merge(TreeNode x,TreeNode y)
{
    TreeNode re;
    re.l=x.l,re.r=y.r,re.len=x.len+y.len,re.size=re.r-re.l+1;
    if (x.len==x.size) re.lx=x.len+y.lx; else re.lx=x.lx;
    if (y.len==y.size) re.rx=y.len+x.rx; else re.rx=y.rx;
    re.ans=max(max(x.ans,y.ans),x.rx+y.lx); re.tag=-1;
    return re;
}
inline void UpDate(int now) {tree[now]=Merge(tree[now<<1],tree[now<<1|1]);}
void BuildTree(int now,int l,int r)
{
    tree[now].l=l,tree[now].r=r,tree[now].size=r-l+1,tree[now].tag=-1;
    tree[now].lx=tree[now].rx=tree[now].ans=tree[now].len=0;
    if (l==r) return;
    int mid=(l+r)>>1;
    BuildTree(now<<1,l,mid); BuildTree(now<<1|1,mid+1,r);
//    UpDate(now);
}
inline void Paint(int now,int c)
{
    if (c==1) tree[now].lx=tree[now].rx=tree[now].ans=tree[now].len=0;
        else tree[now].lx=tree[now].rx=tree[now].ans=tree[now].len=tree[now].size;
    tree[now].tag=c;
}
inline void PushDown(int now)
{
    if (tree[now].tag==-1) return;
    Paint(now<<1,tree[now].tag); Paint(now<<1|1,tree[now].tag);
    tree[now].tag=-1;
}
void DisposalHole(int now,int L,int R,int deal)
{
    if (L<=tree[now].l && R>=tree[now].r) {Paint(now,deal); return;}
    PushDown(now);
    int mid=(tree[now].l+tree[now].r)>>1;
    if (L<=mid) DisposalHole(now<<1,L,R,deal);
    if (R>mid) DisposalHole(now<<1|1,L,R,deal);
    UpDate(now);
}
int Length(int now,int L,int R)
{
    if (L<=tree[now].l && R>=tree[now].r) return tree[now].len;
    PushDown(now);
    int mid=(tree[now].l+tree[now].r)>>1,re=0;
    if (L<=mid) re+=Length(now<<1,L,R);
    if (R>mid) re+=Length(now<<1|1,L,R);
    return re;
}
void FillHole(int now,int L,int R,int cnt)
{
    if (cnt==0) return;
    if (L<=tree[now].l && R>=tree[now].r && tree[now].len<=cnt) {Paint(now,1); return;}
    PushDown(now);
    int mid=(tree[now].l+tree[now].r)>>1,len;
    if (L<=mid)
        if ((len=Length(now<<1,L,R))<cnt)
            {DisposalHole(now<<1,L,R,1); if (R>mid) FillHole(now<<1|1,L,R,cnt-len);}
        else FillHole(now<<1,L,R,cnt);
    else FillHole(now<<1|1,L,R,cnt);
    UpDate(now);
}
TreeNode Query(int now,int L,int R)
{

    if (L<=tree[now].l && R>=tree[now].r) return tree[now];
    PushDown(now);
    int mid=(tree[now].l+tree[now].r)>>1;
    if (R<=mid) return Query(now<<1,L,R);
    else if (L>mid) return Query(now<<1|1,L,R);
    else return Merge(Query(now<<1,L,R),Query(now<<1|1,L,R));
}
int main()
{
    n=read(); m=read();
    BuildTree(1,1,n);
    for (int i=1; i<=m; i++)
        {
            int opt=read(); int l,r,L,R,cnt=0;
            switch (opt)
                {
                    case 0: L=read(),R=read(); DisposalHole(1,L,R,0); break;
                    case 1: l=read(),r=read(),L=read(),R=read(),cnt=r-l+1-Length(1,l,r); DisposalHole(1,l,r,0); FillHole(1,L,R,cnt); break;
                    case 2: L=read(),R=read(); printf("%d\n",Query(1,L,R).ans); break;
                }
        }
    return 0;
}

小号非权限跑的飞快,直冲rank3

大号权限被小号卡rank4,怒加inline,跑的更慢了...

时间: 2024-10-09 07:57:38

【BZOJ-4592】脑洞治疗仪 线段树的相关文章

[SHOI2015]脑洞治疗仪(线段树?珂朵莉树)

题面 这道题超级可爱呢,珂朵莉最可爱了,不,小哀才是最可爱的呢 很好的题,可以考虑用线段树维护,hale表示线段树思路很难,而且难打,不如滚去写珂朵莉树哦 对于操作一:直接将set修改插入即可 对于操作三:最大连续子段和(线段树里面是这样叫的吧)维护即可 对于操作二:我们发现可以考虑先将这段区间里面的1 全部取出来,然后暴力合并区间为0,插入会set里面 之后枚举要修改的区间,从左端点开始搞起,一直后搜索,最后加一个判断,是否已经完全ok即可,具体可参见代码 好了,这道题就解决了 我的代码好像l

BZOJ 4592 脑洞治疗仪

天啦噜我自己YY的从任意起点开始的线段树上二分居然是对的.... 好感动啊. 4.7k的代码只调了一个晚上好感动. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 200500 using namespace std; int n,m,sum[maxn<<2],lazy[maxn<<2],ls[maxn<

bzoj 2120: 数颜色 线段树套平衡树

/************************************************************** Problem: 2120 User: wangyucheng Language: C++ Result: Time_Limit_Exceed ****************************************************************/ #include<iostream> #include<cstdio> #incl

Bzoj 2752 高速公路 (期望,线段树)

Bzoj 2752 高速公路 (期望,线段树) 题目链接 这道题显然求边,因为题目是一条链,所以直接采用把边编上号.看成序列即可 \(1\)与\(2\)号点的边连得是. 编号为\(1\)的点.查询的时候把\(r - 1\)就好了. 这里的期望显然就是路径的平均值. 期望值: \[\dfrac{\sum_{i=l}^r\sum_{j=l}^{r}dis[i][j]}{C_{r-l+1}^2}\] 下面部分可以直接算出: 上面这一部分比较难维护. 考虑每一条边会被走过多少次. \[ans = \su

BZOJ 3252 攻略 线段树

题意:链接 方法:线段树 解析: 闲的随机的题. 看完题后看着好像挺简单的. 既然每个点的权值只会传子树,并且整个图是严格的一棵树,所以应该是跟dfs序有关. 然后去看数据范围. 尼玛HINT是什么鬼. 既然这么说了那就想想怎么做吧=-= 并且因为价值都为正的,所以显然要考虑贪心,挑k条从叶节点到根的所有点权值和最大的k条. 并且每一挑完后都需要更新. 然后有一个性质,每个点至多选一次,也就是说每个点至多被删一次. 并且根节点到叶节点链上的所有点的路径上的点权和是随着深度递增的. 所以显然我们用

bzoj 2962 序列操作(线段树)

题外话 做这道题我整个人都非常的绝望,推了一会发现是线段树裸题,然后调了N久一直是WA 情况是这样的 开始WA的几百毫秒的都是由于我比较SB造成的,可是跑了10几秒的程序我查了N久也查不出错 最后灵机一动把50000改成60000就过了,也不知道为啥T_T Description 一个长度为n的序列,有3种操作 1:区间加c 2:区间取为相反数 3:询问区间中选择c个数相乘的所有方案的和mod19940417的值 Solution 这个操作3非常鬼畜,似乎没啥好的办法,但是仔细推导一番会发现这个

bzoj 1835 基站选址(线段树优化Dp)

Description 题意:有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di 需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci 如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了 如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi 现在的问题是,选择基站的位置,使得总费用最小. Solution 首先可以想到dp,用dp[i][j]表示前i个村庄建了j个通讯站且第j个建在i处 dp[i][j]=min(dp[k][

bzoj 1858 序列操作(线段树)

题外话 本来想练练线段树的,然后发现这题及其蛋疼,要打一坨标记,这是我写过的最长的线段树了= = 然后我很SB的把R打成了r调了一个下午真是蛋疼QvQ Description: 给定一个0/1序列,有如下5个操作: 0:区间赋值为0 1:区间赋值为1 2:区间取反 3:询问区间内1的个数 4:询问区间内最大连续1的个数 Solution 没有操作4这显然就是个SB题,有了操作4我们需要打几个标记 我们需要从左端点开始连续0/1的个数,从右端点开始的连续0/1个数 区间内0/1个数,区间内最大连续

BZOJ 2124等差子序列 线段树&amp;&amp;hash

[题目描述 Description] 给一个 1 到 N 的排列{Ai},询问是否存在 1<=p1<p2<p3<p4<p5<…<pLen<=N(Len>=3),使得 Ap1,Ap2,Ap3,…ApLen 是一个等差序列. [输入描述 Input Description] 输入的第一行包含一个整数 T,表示组数. 下接 T 组数据,每组第一行一个整数 N,每组第二行为一个 1 到 N 的排列, 数字两两之间用空格隔开. [输出描述 Output Desc