LOJ #2037. 「SHOI2015」脑洞治疗仪

#2037. 「SHOI2015」脑洞治疗仪

题目描述

曾经发明了自动刷题机的发明家 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 的问题:在大脑某个区间中最大的连续脑洞区域有多大。

输入格式

第一行两个整数 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]区间内最大的脑洞有多大。

上述区间均在 [1,n] 范围内。

输出格式

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

样例

样例输入

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

样例输出

3
3
6
6

数据范围与提示

对于 20% 的数据,n,m≤100;
对于 50% 的数据,n,m≤20000;
对于 100%的数据,n,m≤200000。

一眼线段树 但是操作太恶心。。。

维护5个域 ml 区间包含左端点最长0串

   mr 区间包含右端点最长0串

     mx 区间最长0串

sum 区间1的个数 len区间长度(这个不一定要维护 用结构体可以不用写。)

两种  tag 标记 tag=1 区间赋值为1

tag=0 区间赋值为 0

一个是区间赋值为 0 简单的区间修改+标记下放

区间查询 1的个数

区间间从左向右 赋值为1   从每个 0的个数小于当前剩余1个个数的区间填数 每次只将一个小区间填满

区间查询最长0串  这里需要一个辅助变量 因为线段树普通查询只是返回每个区间的最大值

如果我们查询了两个区间 目标区间的最大值恰好是左区间的mr+右区间的ml 这种情况是会出错的

就像 1 1 1  0 0 0 1 一共7个数 要查询 [1,5]的1最大值 线段树查询会先找[1,4] 再找 [5,5] 然后返回 两个区间的最大值

但是 [4,5] 是连续的 所以我们用help变量来记录 每个区间从右端点开始的最大值 与ans比较 再更新

  1 #include <cstdio>
  2 #include <cctype>
  3
  4 const int MAXN=200010;
  5
  6 int n,m,ans,help;
  7
  8 struct SegmentTree {
  9     int l,r;
 10     int tag;
 11     int mx,ml,mr;
 12     int sum,len;
 13 };
 14 SegmentTree t[MAXN<<2];
 15
 16 inline void read(int&x) {
 17     int f=1;register char c=getchar();
 18     for(x=0;!isdigit(c);c==‘-‘&&(f=-1),c=getchar());
 19     for(;isdigit(c);x=x*10+c-48,c=getchar());
 20     x=x*f;
 21 }
 22
 23 inline int max(int a,int b) {return a<b?b:a;}
 24
 25 inline void up(int now) {
 26     t[now].sum=t[now<<1].sum+t[now*2+1].sum;
 27     t[now].mx=max(t[now<<1].mx,t[now*2+1].mx);
 28     t[now].ml=t[now<<1].ml;
 29     t[now].mr=t[now*2+1].mr;
 30     if(t[now<<1].ml==t[now<<1].len) t[now].ml=t[now<<1].len+t[now*2+1].ml;
 31     if(t[now*2+1].mr==t[now*2+1].len) t[now].mr=t[now*2+1].len+t[now<<1].mr;
 32     t[now].mx=max((max(t[now].ml,t[now].mr),t[now<<1].mr+t[now*2+1].ml),t[now].mx);
 33 }
 34
 35 inline void down(int now) {
 36     t[now<<1].tag=t[now<<1|1].tag=t[now].tag;
 37     t[now<<1].sum=(t[now<<1].r-t[now<<1].l+1)*t[now].tag;
 38     t[now<<1|1].sum=(t[now<<1|1].r-t[now<<1|1].l+1)*t[now].tag;
 39     if(t[now].tag) t[now<<1].ml=t[now<<1].mr=t[now<<1].mx=0,t[now<<1|1].ml=t[now<<1|1].mr=t[now<<1|1].mx=0;
 40     else t[now<<1].ml=t[now<<1].mr=t[now<<1].mx=t[now<<1].len,t[now<<1|1].ml=t[now<<1|1].mr=t[now<<1|1].mx=t[now<<1|1].len;
 41     t[now].tag=-1;
 42 }
 43
 44 void build_tree(int now,int l,int r) {
 45     t[now].l=l;t[now].r=r;
 46     t[now].len=r-l+1;
 47     t[now].tag=-1;
 48     if(l==r) {
 49         t[now].sum=1;
 50         return;
 51     }
 52     int mid=(l+r)>>1;
 53     build_tree(now<<1,l,mid);
 54     build_tree(now<<1|1,mid+1,r);
 55     t[now].sum=t[now<<1].sum+t[now<<1|1].sum;
 56 }
 57
 58 void modify(int now,int l,int r) {
 59     if(l<=t[now].l&&r>=t[now].r) {
 60         t[now].tag=0;
 61         t[now].sum=0;
 62         t[now].ml=t[now].mr=t[now].mx=t[now].len;
 63         return;
 64     }
 65     if(t[now].tag!=-1) down(now);
 66     int mid=(t[now].l+t[now].r)>>1;
 67     if(l<=mid) modify(now<<1,l,r);
 68     if(r>mid) modify(now<<1|1,l,r);
 69     up(now);
 70 }
 71
 72 void modify2(int now,int l,int r,int &s) {
 73     if(!s) return;
 74     if(l<=t[now].l&&r>=t[now].r&&s>=t[now].len-t[now].sum) {
 75         s-=t[now].len-t[now].sum;
 76         t[now].tag=1;
 77         t[now].sum=t[now].len;
 78         t[now].mx=t[now].ml=t[now].mr=0;
 79         return;
 80     }
 81     if(t[now].tag!=-1) down(now);
 82     int mid=(t[now].l+t[now].r)>>1;
 83     if(l<=mid) modify2(now<<1,l,r,s);
 84     if(r>mid) modify2(now<<1|1,l,r,s);
 85     up(now);
 86 }
 87
 88 void query(int now,int l,int r) {
 89     if(l<=t[now].l&&r>=t[now].r) {
 90         ans=max(ans,t[now].mx);
 91         ans=max(ans,help+t[now].ml);
 92         if(t[now].mr==t[now].len) help+=t[now].len;
 93         else help=t[now].mr;
 94         return;
 95     }
 96     if(t[now].tag!=-1) down(now);
 97     int mid=(t[now].l+t[now].r)>>1;
 98     if(l<=mid) query(now<<1,l,r);
 99     if(r>mid) query(now<<1|1,l,r);
100 }
101
102 int _query(int now,int l,int r) {
103     int ans=0;
104     if(l<=t[now].l&&r>=t[now].r) return t[now].sum;
105     if(t[now].tag!=-1) down(now);
106     int mid=(t[now].l+t[now].r)>>1;
107     if(l<=mid) ans+=_query(now<<1,l,r);
108     if(r>mid) ans+=_query(now<<1|1,l,r);
109     return ans;
110 }
111
112 int hh() {
113     read(n);read(m);
114     build_tree(1,1,n);
115     for(int type,x,y,L,R,i=1;i<=m;++i) {
116         read(type);read(x);read(y);
117         if(!type) modify(1,x,y);
118         else if(type==2) {
119             ans=0,help=0;
120             query(1,x,y);
121             printf("%d\n",ans);
122         }
123         else {
124             read(L);read(R);
125             int t=_query(1,x,y);
126             modify(1,x,y);
127             modify2(1,L,R,t);
128         }
129     }
130     return 0;
131 }
132
133 int sb=hh();
134 int main(int argc,char**argv) {;}

代码

时间: 2024-08-02 06:30:02

LOJ #2037. 「SHOI2015」脑洞治疗仪的相关文章

LibreOJ #2037. 「SHOI2015」脑洞治疗仪

线段树区间合并问题 恶心... 屠龙宝刀点击就送 #include <cstdio> #define N 200005 struct Segment { int l,r,mid,sum,lm,rm,m,len,flag; Segment * ch[2]; Segment () { ch[0]=ch[1]=NULL; lm=rm=m=0; flag=-1; } }*root=new Segment; int n,m,tmp,ans,nowl; inline int max(int a,int b

loj #2036. 「SHOI2015」自动刷题机

link : https://loj.ac/problem/2036 这个显然具有单调性,N小的话更容易A题,不仅因为A一次题减少的代码,并且A题的下限也低. 所以直接上二分就行了,注意上限一定不要设小,不然容易gg. #include<bits/stdc++.h> #define ll long long #define maxn 100005 using namespace std; int a[maxn],n,K; ll l,r,mid,le,ri; inline int calc(){

loj#2552. 「CTSC2018」假面

题目链接 loj#2552. 「CTSC2018」假面 题解 本题严谨的证明了我菜的本质 对于砍人的操作好做找龙哥就好了,blood很少,每次暴力维护一下 对于操作1 设\(a_i\)为第i个人存活的概率,\(d_i\)为死掉的概率,\(g_{i,j}\)是除i以外活了j个人的概率 那个选中i人的答案就是 \[a_i\times\sum_{j = 0} ^{k - 1}\frac{g_{i,j}}{j + 1}\] 对于\(g_{i,j}\) ,设\(f_{i,j}\)表示前\(i\)个人有\(

loj#2076. 「JSOI2016」炸弹攻击 模拟退火

目录 题目链接 题解 代码 题目链接 loj#2076. 「JSOI2016」炸弹攻击 题解 模拟退火 退火时,由于答案比较小,但是温度比较高 所以在算exp时最好把相差的点数乘以一个常数让选取更差的的概率降低 代码 #include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define gc getchar() #define

Loj #2541「PKUWC2018」猎人杀

Loj #2541. 「PKUWC2018」猎人杀 题目链接 好巧妙的题! 游戏过程中,概率的分母一直在变化,所以就非常的不可做. 所以我们将问题转化一下:我们可以重复选择相同的猎人,只不过在一个猎人被选择了过后我们就给他打上标记,再次选择他的时候就无效.这样与原问题是等价的. 证明: 设\(sum=\sum_iw_i,kill=\sum_{i被杀死了}w_i\). 攻击到未被杀死的猎人\(i\)的概率为\(P\). 则根据题意\(P=\frac{w_i}{sum-kill}\). 问题转化后:

Loj #2542. 「PKUWC2018」随机游走

Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次询问给定一个集合 \(S\),求如果从 \(x\) 出发一直随机游走,直到点集 \(S\) 中所有点都至少经过一次的话,期望游走几步. 特别地,点 \(x\)(即起点)视为一开始就被经过了一次. 答案对 $998244353 $ 取模. 输入格式 第一行三个正整数 \(n,Q,x\). 接下来 \(

Loj #2192. 「SHOI2014」概率充电器

Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器,您生 活不可或缺的必需品!能充上电吗?现在就试试看吧!」 SHOI 概率充电器由 \(n-1\) 条导线连通了 \(n\) 个充电元件.进行充电时,每条导线是否可以导电以 概率决定,每一个充电元件自身是否直接进行充电也由概率决定.随后电能可以从直接充电的元件经

Loj #3111. 「SDOI2019」染色

Loj #3111. 「SDOI2019」染色 题目描述 给定 \(2 \times n\) 的格点图.其中一些结点有着已知的颜色,其余的结点还没有被染色.一个合法的染色方案不允许相邻结点有相同的染色. 现在一共有 \(c\) 种不同的颜色,依次记为 \(1\) 到 \(c\).请问有多少对未染色结点的合法染色方案? 输入格式 第一行有两个整数 \(n\) 和 \(c\),分别描述了格点图的大小和总的颜色个数. 之后两行,每行有 \(n\) 个整数:如果是 \(0\) 则表示对应结点未被染色,否

Loj #2553. 「CTSC2018」暴力写挂

Loj #2553. 「CTSC2018」暴力写挂 题目描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = 0\) 的部分分是求树 \(T\) 上的最长链.可怜的 temporaryDO 并不会做这道题,他在考场上抓猫耳挠猫腮都想不出一点思路. 这时,善良的板板出现在了空中,他的身上发出璀璨却柔和的光芒,荡漾在考场上.''题目并不难.'' 板板说.那充满磁性的声音,让 temporaryDO 全身充满了力量. 他