前言

这道题目呢,看上去很难,实际上我们可以用线段树解决这道题目。

正文

我们维护 sumlentaglmaxrmaxans

sum 就是这段区间非脑洞的个数

len 就是这段区间的长度

tag 就是我们的 lazy_tag

lmax 就是从左开始的连续脑洞个数

rmax 就是从右开始的连续脑洞个数

ans 就是这段区间最大的连续脑洞

建树

由于 len 是不变的,所以我们可以建树的时候就求出 len

t[num].len=r-l+1;

pushup

sum

sum 就是左子树和右子树的 sum 的和。

t[num].sum=t[ls].sum+t[rs].sum;

lmax

lmax 的话有两种情况

第 \(1\) 种情况

lmax=左子树的 lmax

第 \(2\) 中情况

lmax=左子树的 len + 右子树的 lmax

if(t[ls].lmax==t[ls].len)t[num].lmax=t[ls].len+t[rs].lmax;
else t[num].lmax=t[ls].lmax;

rmax

rmax 的话也两种情况

第 \(1\) 种情况

rmax=右子树的 rmax

lmax=右子树的 len + 左子树的 rmax

if(t[rs].rmax==t[rs].len)t[num].rmax=t[rs].len+t[ls].rmax;
else t[num].rmax=t[rs].rmax;

ans

ans 的话有 \(3\) 种情况

第 \(1\) 种情况

ans=左子树的 ans

第 \(2\) 种情况

ans=右子树的 ans

第 \(3\) 种情况

ans=左子树的 rmax+右子树的 lmax

t[num].ans=max(max(t[ls].ans,t[rs].ans),t[ls].rmax+t[rs].lmax);

pushdown

tag

我们的 tag3 种值,分别为 012

0 表示什么都没有

1 表示全部为脑洞

2 表示全部不为脑洞

0

0 的话,代表没有任何操作,不要管。

1

我们对照上面的发现:

anslmaxrmax 都为 len

sum 则为 0

tag 的标记当然要打啦。

void down1(int num){
t[num].ans=t[num].lmax=t[num].rmax=t[num].len;
t[num].sum=0;
t[num].tag=1;
}
2

我们对照上面的发现:

anslmaxrmax 都为 0

sum 则为 len

tag 的标记当然要打啦。

void down2(int num){
t[num].ans=t[num].lmax=t[num].rmax=0;
t[num].sum=t[num].len;
t[num].tag=2;
}

二分

我们可以发现,操作 2 就是先统计一遍 \([l0,r0]\) 中非脑洞的个数。

然后把 \([l0,r0]\) 这段区间全部变成脑洞,再去在 \([l1,r1]\) 这段区间里找到从 \(l0\) 开始算起最右边脑洞个数 \(\leq[l0,r0]\) 中脑洞的个数。

我们发现脑洞个数是单调递增的,所以我们可以二分。

我采用的写法是左闭右开。

void work(){
int x=query0(1,l0,r0);//统计
if(x==0)return;//这里要注意,否则我们的边界就是错的
change(1,l0,r0,1);//全部变成脑洞
int l=l1,r=r1+1;//二分的边界
while(l+1<r){//经典写法
int mid=(l+r)>>1;//求mid
if(query1(1,l1,mid)<=x)l=mid;//小于等于
else r=mid;
}
change(1,l1,l,2);//填上去
}

代码

复杂度 \(O(n \log n + q \log^2 n)\)

#include <bits/stdcpp.h>
#define ls num<<1
#define rs num<<1|1
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
template<typename T>inline void write(T x){
if(x<0)putchar('-'),x*=-1;
if(x>9)write(x/10);
putchar(x%10+48);
}
template<typename T>inline void writen(T x){
write(x);
puts("");
}
const int N=2e5+10;
struct Tree{
int l,r,lmax,rmax,sum,tag,len,ans;
}t[N<<2];
int n,m,l0,r0,l1,r1,f;
void pushup(int num){
t[num].sum=t[ls].sum+t[rs].sum;
if(t[ls].lmax==t[ls].len)t[num].lmax=t[ls].len+t[rs].lmax;
else t[num].lmax=t[ls].lmax;
if(t[rs].rmax==t[rs].len)t[num].rmax=t[rs].len+t[ls].rmax;
else t[num].rmax=t[rs].rmax;
t[num].ans=max(max(t[ls].ans,t[rs].ans),t[ls].rmax+t[rs].lmax);
}
void down1(int num){
t[num].ans=t[num].lmax=t[num].rmax=t[num].len;
t[num].sum=0;
t[num].tag=1;
}
void down2(int num){
t[num].ans=t[num].lmax=t[num].rmax=0;
t[num].sum=t[num].len;
t[num].tag=2;
}
void pushdown(int num){
if(t[num].tag==1){
down1(ls);down1(rs);
t[num].tag=0;
}
if(t[num].tag==2){
down2(ls);down2(rs);
t[num].tag=0;
}
}
void build(int num,int l,int r){
t[num].tag=0;
t[num].l=l;
t[num].r=r;
t[num].len=r-l+1;
if(l==r){
t[num].sum=1;
t[num].ans=t[num].lmax=t[num].rmax=0;
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(num);
}
void change(int num,int x,int y,int z){
if(t[num].l>=x&&t[num].r<=y){
if(z==1)down1(num);
if(z==2)down2(num);
return;
}
pushdown(num);
if(t[ls].r>=x)change(ls,x,y,z);
if(t[rs].l<=y)change(rs,x,y,z);
pushup(num);
}
int query0(int num,int x,int y){
if(t[num].l>=x&&t[num].r<=y)return t[num].sum;
pushdown(num);
if(t[ls].r<x)return query0(rs,x,y);
if(t[rs].l>y)return query0(ls,x,y);
return query0(ls,x,y)+query0(rs,x,y);
}
int query1(int num,int x,int y){
if(t[num].l>=x&&t[num].r<=y)return t[num].len-t[num].sum;
pushdown(num);
if(t[ls].r<x)return query1(rs,x,y);
if(t[rs].l>y)return query1(ls,x,y);
return query1(ls,x,y)+query1(rs,x,y);
}
void work(){
read(l1);read(r1);
int x=query0(1,l0,r0);
if(x==0)return;
change(1,l0,r0,1);
int l=l1,r=r1+1;
while(l+1<r){
int mid=(l+r)>>1;
if(query1(1,l1,mid)<=x)l=mid;
else r=mid;
}
change(1,l1,l,2);
}
int query2(int num,int x,int y){
if(t[num].l>=x&&t[num].r<=y)return t[num].ans;
pushdown(num);
if(t[ls].r<x)return query2(rs,x,y);
if(t[rs].l>y)return query2(ls,x,y);
return max(max(query2(ls,x,y),query2(rs,x,y)),min(t[ls].rmax,t[rs].l-x)+min(t[rs].lmax,y-t[ls].r));
}
int main(){
read(n);read(m);
build(1,1,n);
while(m--){
read(f);read(l0);read(r0);
switch(f){
case 0:change(1,l0,r0,1);break;
case 1:work();break;
case 2:writen(query2(1,l0,r0));break;
}
}
return 0;
}

拓展

这道题目还有更优秀的解法,复杂度可以少掉一个 \(\log\) 也就是变成 \(O(n \log n+q \log{n})\)。

我们还是先统计非脑洞个数。

我们写一个函数 \(fill\) 就是我们用来把脑细胞填入脑洞的函数。我们要填 \(x\) 个脑细胞,会发现有 \(2\) 种情况。

  • 第 \(1\) 种情况是所有脑细胞都填入左子树。

  • 第 \(2\) 种情况是所有脑细胞不仅把左边填满,还有多的放到右子树。

我们可以根据这个写代码:

int fill(int num,int l,int r,int x){//fill的返回值就是剩余的脑细胞数量
if(x==0)return 0;
if(t[num].l>=l&&t[num].r<=r&&t[num].sum<=x){
int s=t[num].sum;//务必要先存起来
down2(num);
return x-s;
}
pushdown(num);int ans;
if(t[ls].r<l)ans=fill(rs,l,r,x);
else if(t[rs].l>r)ans=fill(ls,l,r,x);
else ans=fill(rs,l,r,fill(ls,l,r,x));
pushup(num);
return ans;//答案
}

题解 P4344 【[SHOI2015]脑洞治疗仪】的更多相关文章

  1. 【题解】Luogu P4344 [SHOI2015]脑洞治疗仪

    原题传送门:P4344 [SHOI2015]脑洞治疗仪 前置芝士:珂朵莉树 窝博客里对珂朵莉树的介绍 没什么好说的自己看看吧 珂朵莉树好题啊 我一开始一直Re65 后来重构代码就ac了,或许是rp问题 ...

  2. 洛谷P4344 [SHOI2015]脑洞治疗仪(珂朵莉树)

    传送门 看到区间推倒……推平就想到珂朵莉树 挖脑洞直接assign,填坑先数一遍再assign再暴力填,数数的话暴力数 //minamoto #include<iostream> #inc ...

  3. 洛谷 P4344 [SHOI2015]脑洞治疗仪

    题意简述 维护序列,支持以下操作: 0 l r:将l~r赋为0 1 l1 r1 l2 r2:将l1~r1中的1替换l2~r2中的0,多余舍弃 2 l r:询问l~r中最大连续1的长度 题解思路 珂朵莉 ...

  4. 洛谷P4344 [SHOI2015]脑洞治疗仪(ODT)

    题意 题目链接 Sol ODT板子题. 操作1直接拆区间就行. #include<bits/stdc++.h> #define fi first #define se second con ...

  5. 【BZOJ4592】[Shoi2015]脑洞治疗仪 线段树

    [BZOJ4592][Shoi2015]脑洞治疗仪 Description 曾经发明了自动刷题机的发明家SHTSC又公开了他的新发明:脑洞治疗仪--一种可以治疗他因为发明而日益增大的脑洞的神秘装置. ...

  6. [SHOI2015]脑洞治疗仪(恶心的线段树,区间最大子段和)

    题目描述: 曾经发明了自动刷题机的发明家 SHTSC 又公开了他的新发明:脑洞治疗仪--一种可以治疗他因为发明而日益增大的脑洞的神秘装置. 为了简单起见,我们将大脑视作一个 01 序列.11代表这个位 ...

  7. bzoj千题计划280:bzoj4592: [Shoi2015]脑洞治疗仪

    http://www.lydsy.com/JudgeOnline/problem.php?id=4592 注意操作1 先挖再补,就是补的范围可以包含挖的范围 SHOI2015 的题 略水啊(逃) #i ...

  8. 【[SHOI2015]脑洞治疗仪】

    我太sb啦 合并的时候又漏了,又漏了,又漏了 我个sb 这是个板子题,并不知道为什么SHOI2015会考这么板子的题,但是我又sb了,又sb了,又sb了,又没有1A 显然我是凉了 这道题有三个操作 区 ...

  9. bzoj 4592(洛谷 4344) [Shoi2015]脑洞治疗仪——线段树上二分

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4592 1操作就是用线段树来二分找到第一个有 k 个0的位置. 在洛谷上A了,与暴力和网上题解 ...

随机推荐

  1. ES:在线迁移集群索引,数据不丢失

    一.背景 生产环境由于某些原因需要跨机房迁移ES集群,或者同机房原有集群中所有节点全部更换,期间ES索引要求完整,客户端请求中断不超过五分钟. 二.应用场景 1.同机房不同集群之间数据迁移: 2.跨机 ...

  2. Leetcode 142题 环形链表 II(Linked List Cycle II) Java语言求解

    题目描述: 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 p ...

  3. C++走向远洋——39(指向学生类的指针)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:zhizhen.cpp * 作者:常轩 * 微信公众号:Worl ...

  4. 5——PHP逻辑运算符&&唯一的三元运算符

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  5. 5,Hadoop中的文件

    1,文件结构 · bin:脚本和命令目录. · etc:配置文件目录. · sbin:命令目录,主要包含HDFS和YARN中各类服务的启动和关闭,依赖于bin中的脚本. · share:各个模块编译后 ...

  6. 浅谈.NET Framework 与CLR

            承载公共语言运行时 (CLR) 的所有应用程序均需启动(或称“激活”)CLR 以运行托管代码.             通常,.NET Framework 应用程序在生成它的 CLR ...

  7. 前端每日实战:26# 视频演示如何用不到 50 行 CSS 代码,创作按钮被从纸上掀起的立体效果

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/KRbXGe 可交互视频教程 此视频 ...

  8. 前端面试题(HTML、CSS部分)

    HTML.CSS部分: 一.html5有哪些新特性.移除了那些元素?如何处理HTML5新标签的浏览器兼容问题?如何区分 HTML 和 HTML5?   新特性: HTML5 现在已经不是 SGML 的 ...

  9. 最新版jdk 13环境变量配置

    1.配置环境变量 右击“我的电脑”-->"属性"-->"高级系统设置"-->"高级"-->"环境变量&qu ...

  10. 免ROOT卸载手机自带软件详细教程

    一.准备条件 1.电脑一台 2.手机一部 3.WiFi 二.下载所需资源 微信扫码进入搜索,选择安卓软件卸载工具 根据图中提示,按照自己的系统进行下载 三.下载完后解压(以Windows为例),解压后 ...