1 条题解
-
0
自动搬运
来自洛谷,原作者为

kradcigam
永不放弃之心,将成为贯穿逆境之光!搬运于
2025-08-24 21:59:13,当前版本为作者最后更新于2020-03-27 10:43:39,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
前言
这道题目呢,看上去很难,实际上我们可以用线段树解决这道题目。
正文
我们维护
sum、len、tag、lmax、rmax、ans。sum就是这段区间非脑洞的个数len就是这段区间的长度tag就是我们的lazy_taglmax就是从左开始的连续脑洞个数rmax就是从右开始的连续脑洞个数ans就是这段区间最大的连续脑洞建树
由于
len是不变的,所以我们可以建树的时候就求出lent[num].len=r-l+1;pushupsumsum就是左子树和右子树的sum的和。t[num].sum=t[ls].sum+t[rs].sum;lmaxlmax的话有两种情况第 种情况

lmax=左子树的lmax第 中情况

lmax=左子树的len+ 右子树的lmaxif(t[ls].lmax==t[ls].len)t[num].lmax=t[ls].len+t[rs].lmax; else t[num].lmax=t[ls].lmax;rmaxrmax的话也两种情况第 种情况

rmax=右子树的rmax
lmax=右子树的len+ 左子树的rmaxif(t[rs].rmax==t[rs].len)t[num].rmax=t[rs].len+t[ls].rmax; else t[num].rmax=t[rs].rmax;ansans的话有 种情况第 种情况

ans=左子树的ans第 种情况

ans=右子树的ans第 种情况

ans=左子树的rmax+右子树的lmaxt[num].ans=max(max(t[ls].ans,t[rs].ans),t[ls].rmax+t[rs].lmax);pushdowntag我们的
tag有3种值,分别为0,1,20表示什么都没有1表示全部为脑洞2表示全部不为脑洞00的话,代表没有任何操作,不要管。1我们对照上面的发现:
ans、lmax、rmax都为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我们对照上面的发现:
ans、lmax、rmax都为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就是先统计一遍 中非脑洞的个数。然后把 这段区间全部变成脑洞,再去在 这段区间里找到从 开始算起最右边脑洞个数 中脑洞的个数。
我们发现脑洞个数是单调递增的,所以我们可以二分。
我采用的写法是左闭右开。
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);//填上去 }代码
复杂度
#include <bits/stdc++.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; }拓展
这道题目还有更优秀的解法,复杂度可以少掉一个 也就是变成 。
我们还是先统计非脑洞个数。
我们写一个函数 就是我们用来把脑细胞填入脑洞的函数。我们要填 个脑细胞,会发现有 种情况。
-
第 种情况是所有脑细胞都填入左子树。
-
第 种情况是所有脑细胞不仅把左边填满,还有多的放到右子树。
我们可以根据这个写代码:
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;//答案 }感谢 @LightningUZ 的补充!
-
- 1
信息
- ID
- 3308
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者