当前位置: 首页 > news >正文

洛谷 P2572 [SCOI2010] 序列操作 题解(线段树)

前言

P2572这道题真的相当折磨,相当难调了。希望这一篇题解可以帮到你。如果畏惧了超巨的码量请移步神仙压行。

本题解已尽最大努力肢解整一份代码,所以比较长。

线段树的构成

num[N],lazy[N]; //1的数量 num1[N];//连续1的数量 num0[N];//连续0的数量 lx[N],ly[N];//当前区间最左侧和最右侧的1/0连续数量(正数/负数) 

lx,ly函数用于判断两个区间的1能否合并。正数表示连续1的数量,负数表示连续0的数量。

操作0~2的change函数

操作0

将整个区间直接赋值为0
那么区间1的数量和连续1的数量均变成0
区间连续0的数量为区间总长度。
当前最左、最右的0连续数量也为区间总长度,但是由于是0的连续数量所以值为负数。


num[id]=0;num1[id]=0;
num0[id]=r-l+1;lx[id]=ly[id]=-(r-l+1);//0是负数lazy[id]=z;	//lazy标记

操作1

将整个区间直接赋值为1
那么区间1的数量和连续1的数量均变成区间总长度
区间连续0的数量为0
当前最左、最右的1连续数量也为区间总长度,但是由于是1的连续数量所以值为正数。


num[id]=(r-l+1);num1[id]=r-l+1;
num0[id]=0;lx[id]=ly[id]=r-l+1;lazy[id]=z; 

操作2

将整个区间的数字取反。
那么01的数量将会调转。
所以区间1的总数就是,区间0的总数,即区间总长度减去1的总数。
因为01调转,所以本来的连续0变成了1,1变成0

num[id]=r-l+1-num[id];swap(num1[id],num0[id]);//取反的话 最长连续1 和 最长连续0 正好调转lx[id]*=-1;//取反的话 连续长度 但是正负要变 
ly[id]*=-1; 

关于操作2的lazy

在进行取反操作前如果有赋值操作那么要先进行赋值操作。
比如之前全赋值为1取反之后相当于全赋值为0
进行取反的lazy标记时记得取反是可以叠加的。两次取反操作等于没操作。

if(lazy[id]==1)
{lazy[id]=2;
}
else if(lazy[id]==2)
{lazy[id]=1;
}
else
{lazy[id]+=z;//取反状态不是可重复的是叠加的
}

查询3的query函数

简单的求和函数。
代码就不放了

查询4的ans函数

查询连续1的数量时不能直接查询求max。需要考虑拼接的情况。所以需要下面两个区间的lx ly的值。所以我们可以返回直接一个结构体。

下放函数pushdown

操作原理是和change函数一样的,不再赘述。

pushup函数

pushup函数关键是要考虑清楚两个区间如何合并。
我们举几个例子就好了。
比如合并区间[1,5]和[6,10]。
数字分别为1001110010
lx,ly分别为1,21,-1
判断能否拼接只需要左半部分的最右侧(ly),和右半部分的最左侧(lx)是否同号即可。
那么大区间的lx继承左半部分的lx,ly继承右半部分ly

当然会有一种特殊情况。如果某半边全是1或者0。那么lx ly不应仅是继承,应该是合并。

代码

#include<bits/stdc++.h>
#include<cstring>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include<map>
#define ll long long
#define lhs printf("\n");
#define sync std::ios::sync_with_stdio(false),std::cin.tie(0),std::cout.tie(0);
using namespace std;
const int N=3e6+10;
const int M=2021;
const int inf=0x3f3f3f3f;
int n,m;
int a[N];
int num[N],lazy[N]; //1的数量 
int num1[N];//连续1的数量 
int num0[N];//连续0的数量 
int lx[N],ly[N];//当前区间最左侧和最右侧的1/0连续数量(正数/负数) 
struct node//用于查询连续1的数量时 
{int lx=0,ly=0,num=0;
};
void pushup(int id,int l,int r)
{int mid=(l+r)/2;num[id]=num[id*2]+num[id*2+1];if(ly[id*2]>0 and lx[id*2+1]>0)//1可以拼起来{num1[id]=max(num1[id*2],max(num1[id*2+1],ly[id*2]+lx[id*2+1])); } else{num1[id]=max(num1[id*2],num1[id*2+1]);}if(ly[id*2]<0 and lx[id*2+1]<0)//0可以拼起来{num0[id]=max(num0[id*2],max(num0[id*2+1],-ly[id*2]-lx[id*2+1])); } else{num0[id]=max(num0[id*2],num0[id*2+1]);}if(lx[id*2]==mid-l+1 and lx[id*2+1]>0)//左下满了1 且 右下最左是1  那么拼在一起 {lx[id]=lx[id*2]+lx[id*2+1];} else if(lx[id*2]==-(mid-l+1) and lx[id*2+1]<0)//左下满了0 且 右下最左是0  那么拼在一起 {lx[id]=lx[id*2]+lx[id*2+1];} else{lx[id]=lx[id*2];}if(ly[id*2+1]==r-mid and ly[id*2]>0) //右下满了1 且 左下最有也是1 {ly[id]=ly[id*2+1]+ly[id*2];}else if(ly[id*2+1]==-(r-mid) and ly[id*2]<0)//右下全是0 且 左下最右也是0 {ly[id]=ly[id*2+1]+ly[id*2]; } else{ly[id]=ly[id*2+1];} 
}
void pushdown(int id,int l,int r)
{if(lazy[id]){int mid=(l+r)/2;if(lazy[id]==1)//全变0 {num[id*2]=0;num0[id*2]=mid-l+1;num1[id*2]=0; lx[id*2]=ly[id*2]=-(mid-l+1); lazy[id*2]=1;num[id*2+1]=0;num0[id*2+1]=r-mid; num1[id*2+1]=0;lx[id*2+1]=ly[id*2+1]=-(r-mid); lazy[id*2+1]=1 ; }else if(lazy[id]==2)//全变1 {num[id*2]=mid-l+1;num0[id*2]=0;num1[id*2]=mid-l+1; lx[id*2]=ly[id*2]=(mid-l+1); lazy[id*2]=2;num[id*2+1]=r-mid;num0[id*2+1]=0;num1[id*2+1]=r-mid;lx[id*2+1]=ly[id*2+1]=(r-mid); lazy[id*2+1]=2;}else if(lazy[id]%3==0 and lazy[id]%2==1)//取反 {num[id*2]=mid-l+1-num[id*2];swap(num0[id*2],num1[id*2]);lx[id*2]*=-1;ly[id*2]*=-1; if(lazy[id*2]==1)//需要先进行lazy的赋值下放操作 {lazy[id*2]=2;}else if(lazy[id*2]==2){lazy[id*2]=1;}else{lazy[id*2]+=3;}num[id*2+1]=r-mid-num[id*2+1];swap(num0[id*2+1],num1[id*2+1]);lx[id*2+1]*=-1;ly[id*2+1]*=-1; if(lazy[id*2+1]==1)//需要先进行lazy的赋值下放操作 {lazy[id*2+1]=2;}else if(lazy[id*2+1]==2){lazy[id*2+1]=1;}else{lazy[id*2+1]+=3;}}lazy[id]=0; }
}
void build(int id,int l,int r)
{if(l==r){num[id]=a[l];if(a[l])//1{num1[id]=1;lx[id]=ly[id]=1;}else//0{num0[id]=1;lx[id]=ly[id]=-1;} return; } int mid=(l+r)/2;build(id*2,l,mid);build(id*2+1,mid+1,r);pushup(id,l,r); 
}
void change(int id,int l,int r,int x,int y,int z)
{if(x<=l and r<=y){if(z==1)//0{num[id]=0;num1[id]=0;num0[id]=r-l+1;lx[id]=ly[id]=-(r-l+1);lazy[id]=z;	} else if(z==2)//1{num[id]=(r-l+1);num1[id]=r-l+1;num0[id]=0;lx[id]=ly[id]=r-l+1;lazy[id]=z; }else//取反 {num[id]=r-l+1-num[id];swap(num1[id],num0[id]);//取反的话 最长连续1 和 最长连续0 正好调转lx[id]*=-1;//取反的话 连续长度 但是正负要变 ly[id]*=-1; if(lazy[id]==1){lazy[id]=2;}else if(lazy[id]==2){lazy[id]=1;}else{lazy[id]+=z;//取反状态不是可重复的是叠加的}}return;} pushdown(id,l,r); int mid=(l+r)/2;if(x<=mid){change(id*2,l,mid,x,y,z);} if(mid+1<=y){change(id*2+1,mid+1,r,x,y,z);}pushup(id,l,r);
} int query(int id,int l,int r,int x,int y)//1数量 
{if(x<=l and r<=y){return num[id];}pushdown(id,l,r);int mid=(l+r)/2;int sum=0;if(x<=mid){sum+=query(id*2,l,mid,x,y); }if(mid+1<=y){sum+=query(id*2+1,mid+1,r,x,y);}return sum;
}
node ans(int id,int l,int r,int x,int y)//连续1数量
{if(x<=l and r<=y){int aa=lx[id],bb=ly[id],cc=num1[id];node xx;xx.lx=aa;xx.ly=bb;xx.num=cc;return xx;}	pushdown(id,l,r);int mid=(l+r)/2;int maxx=0;node xx,yy;int flag1=0,flag2=0;if(x<=mid){xx=ans(id*2,l,mid,x,y);flag1=1;}if(mid+1<=y){yy=ans(id*2+1,mid+1,r,x,y);flag2=1;}int lxx,lyy;if(x<=mid and mid+1<=y and xx.ly>0 and yy.lx>0) //1可以拼起来 {if(xx.lx==mid-l+1 and yy.lx>0)//左下满了1 且 右下最左是1  那么拼在一起 {lxx=xx.lx+yy.lx;} else{lxx=xx.lx;}if(yy.ly==r-mid and xx.ly>0) //右下满了1 且 左下最有也是1 {lyy=xx.ly+yy.ly;}else{lyy=yy.ly;} int aa=lxx,bb=lyy,cc=max(xx.num,max(yy.num,xx.ly+yy.lx));node s;s.lx=aa;s.ly=bb;s.num=cc;return s; }if(flag1 and flag2)//两边都访问到了那么取大的那边 {int aa=xx.lx,bb=yy.ly,cc=max(xx.num,yy.num);node s;s.lx=aa;s.ly=bb;s.num=cc;return s;}else//否则只取一边 {if(flag1){return xx;	} else{return yy;}}
}
int main()
{synccin>>n>>m;for(int i=1;i<=n;i++)cin>>a[i];build(1,1,n); for(int i=1;i<=m;i++){int op,x,y;cin>>op>>x>>y;x++;y++;	if(op==0){change(1,1,n,x,y,op+1); }else if(op==1){change(1,1,n,x,y,op+1);}else if(op==2){change(1,1,n,x,y,op+1);}else if(op==3){cout<<query(1,1,n,x,y)<<"\n"; }	 else{cout<<ans(1,1,n,x,y).num<<"\n";}}return 0;
}
/*
10 10
1 0 0 0 1 0 1 1 0 1 
0 6 8
1 0 8
2 1 7
2 6 6
4 6 8
1 6 6
4 6 7
0 0 6
1 5 7
4 9 9答案:1 1 1  
*/
http://www.dtcms.com/a/296777.html

相关文章:

  • 高可用架构模式——如何应对接口级的故障
  • R拟合 | 一个分布能看到三个峰,怎么拟合出这三个正态分布的参数? | 高斯混合模型 与 EM算法
  • Android tcp socket sample示例
  • 实时云渲染将UE像素流嵌入业务系统,实现二维管理系统与数字孪生三维可视化程序的无缝交互
  • MySQL如何处理并发访问和高负载?
  • qlib Alpha360 因子列表解读
  • C++与WebAssembly打造跨平台游戏
  • 《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——4. 前后端联动:打通QML与C++的任督二脉
  • 计算机网络摘星题库800题笔记
  • 单片机按键的控制
  • 西门子plc IEC_TIMER 与 TON_TIME 异同
  • 【STM32】FreeRTOS 任务的创建(二)
  • ESP32+MicroPython:嵌入式开发的极简入门指南
  • MySQL基础02
  • JavaScript 入门教程
  • 【MySQL】MySQL 事务和锁详解
  • Linux应用开发基础知识——进程学习2(exec函数、system函数、popen函数)(三)
  • C语言:20250724笔记(函数-指针)
  • S7-200 SMART 编程基础详解:从语言、数据结构到寻址方式
  • Spring Bean生命周期七步曲:定义、实例化、初始化、使用、销毁
  • C语言(十)
  • 面试150 寻找峰值
  • Leetcode-.42接雨水
  • 【Linux基础知识系列】第六十四篇 - 了解Linux的硬件架构
  • 【学习】数字化车间与智能工厂如何推进制造业转型
  • 放款需要注意的事项
  • C++实战:人脸识别7大核心实例
  • 【Java】空指针(NullPointerException)异常深度攻坚:从底层原理到架构级防御,老司机的实战经验
  • 网络测试工具
  • UE 加载本地Json文件