luogu P2710 数列
(这是个双倍经验呀!
题目描述
维护一个可以支持插入、删除、翻转、区间赋值、求和、求值和求最大子段和操作的序列。(真·简洁)
solution
基本不用什么神奇操作,平衡树硬上就行。(我用的 Splay )
不太一样的是建树可以模仿线段树建法(据说会快一点)
其中,
1.翻转,区间赋值来打两种标记。
2.求最大子段和要设最大子段和,最大前缀和最大后缀三种变量。(不太清楚的可以先去做做小鲦逛公园)
再注意一下,
1.下传标记时,先区间赋值再翻转。
(因为区间赋值改变 \(val\) ,翻转改变 \(id\) , \(id\) 要影响 \(val\) )
2.翻转时,最大前缀和最大后缀要交换。
(因为翻转后,后面的到了前面,前面的到了后面,顺序改变了)
3.平时讲的把一个区间 \([l,r]\) 拉出来是通过把区间外前面的第一个点拉到根后再将区间外后面的第一个点拉到根右孩子树的根上。
实际操作时下标要集体右移一位,因为如果区间为整个数组时要把 \(0\) 拉到根上去,那不就乱了吗!
其实上述情况打文艺平衡树的时候就有了,但再次注意的是这题输入的 \(posi\) 和 \(tot\) 也要在表示区间的时候进行下标的转化。
具体的看看代码吧(马蜂可看,比较清奇,但是长死了,有点烦)
#include<bits/stdc++.h>
#define ls(i) spl[i].ch[0]//左孩子
#define rs(i) spl[i].ch[1]//右孩子
#define fa(i) spl[i].fa//粑粑
#define val(i) spl[i].val//点权值
#define num(i) spl[i].num//子树权值和
#define siz(i) spl[i].siz//字数大小
#define lzy(i) spl[i].lzy//翻转标记
#define mdf(i) spl[i].mdf//区间赋值标记
#define mx(i) spl[i].mx//最大子段和
#define lmx(i) spl[i].lmx//最大前缀
#define rmx(i) spl[i].rmx//最大后缀
//看把孩子逼的
#define reg register
using namespace std;
typedef long long ll;
const int N=1e6+10;
const int INF=0x3f3f3f3f;
int n,m,root,cnt,a[N],id[N];
char s[24];
queue<int> que;
struct Splay{int ch[2],fa,val,num,siz,lzy,mdf,mx,lmx,rmx;}spl[N];
inline int imax(int a,int b){return a>b?a:b;}
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline void pushup(int now){
siz(now)=siz(ls(now))+siz(rs(now))+1;
num(now)=num(ls(now))+num(rs(now))+val(now);
int plu=rmx(ls(now))+lmx(rs(now))+val(now);
mx(now)=imax(imax(mx(ls(now)),mx(rs(now))),plu);
lmx(now)=imax(lmx(ls(now)),lmx(rs(now))+num(ls(now))+val(now));
rmx(now)=imax(rmx(rs(now)),rmx(ls(now))+num(rs(now))+val(now));
}
inline void pushdown(int now){
if(mdf(now)){
mdf(now)=lzy(now)=0;
if(ls(now)){
mdf(ls(now))=1;val(ls(now))=val(now);
num(ls(now))=siz(ls(now))*val(ls(now));
}
if(rs(now)){
mdf(rs(now))=1;val(rs(now))=val(now);
num(rs(now))=siz(rs(now))*val(rs(now));
}
if(val(now)>0){
if(ls(now))mx(ls(now))=lmx(ls(now))=rmx(ls(now))=num(ls(now));
if(rs(now))mx(rs(now))=lmx(rs(now))=rmx(rs(now))=num(rs(now));
}
else {
if(ls(now))mx(ls(now))=val(ls(now)),lmx(ls(now))=rmx(ls(now))=0;
if(rs(now))mx(rs(now))=val(rs(now)),lmx(rs(now))=rmx(rs(now))=0;
}
}
if(lzy(now)){
lzy(ls(now))^=1;lzy(rs(now))^=1;lzy(now)^=1;
swap(ls(ls(now)),rs(ls(now)));
swap(ls(rs(now)),rs(rs(now)));
swap(lmx(ls(now)),rmx(ls(now)));
swap(lmx(rs(now)),rmx(rs(now)));
}
}
inline void rotate(int now){
int nxt=fa(now),nnt=fa(nxt);
int k1=rs(nxt)==now,k2=rs(nnt)==nxt;
int pre=spl[now].ch[k1^1];
spl[nnt].ch[k2]=now; fa(now)=nnt;
spl[nxt].ch[k1]=pre; fa(pre)=nxt;
spl[now].ch[k1^1]=nxt; fa(nxt)=now;
pushup(nxt);pushup(now);
}//虽然肯定会跑得慢,但真tm好看
inline void splay(int now,int S){
while(fa(now)!=S){
int nxt=fa(now),nnt=fa(nxt);
int k1=rs(nxt)==now,k2=rs(nnt)==nxt;
if(nnt!=S)(k1^k2)?rotate(now):rotate(nxt);
rotate(now);
}
if(!S)root=now;
}
inline void build(int lt,int rt,int mi){
if(lt>rt)return ;
int mid=(lt+rt)>>1,now=id[mid],pre=id[mi];
if(lt==rt){
siz(now)=1;
if(a[lt]>0)mx(now)=lmx(now)=rmx(now)=a[lt];
else mx(now)=a[lt],lmx(now)=rmx(now)=0;
}
else {
build(lt,mid-1,mid);
build(mid+1,rt,mid);
}
val(now)=a[mid];fa(now)=pre;
pushup(now);
spl[pre].ch[mid>=mi]=now;
}
inline int find(int now,int siz){
pushdown(now);
int sizz=siz(ls(now));
if(siz<=sizz)return find(ls(now),siz);
else if(siz==sizz+1)return now;
else return find(rs(now),siz-sizz-1);
}
inline void insert(int now,int sum){
for(int i=1;i<=sum;++i){
a[i]=read();
if(!que.empty()){
id[i]=que.front();
que.pop();
}
else {
id[i]=++cnt;
}
}
build(1,sum,0);
int mid=(1+sum)>>1,it=id[mid];
int lt=find(root,now+1);
int rt=find(root,now+2);
splay(lt,0);splay(rt,lt);
ls(rt)=it;fa(it)=rt;
pushup(rt);pushup(root);
}
inline void split(int now){
if(!now)return ;
split(ls(now));split(rs(now));
que.push(now);
ls(now)=rs(now)=val(now)=num(now)=siz(now)=0;
lzy(now)=mdf(now)=mx(now)=lmx(now)=rmx(now)=0;
}
inline int divid(int lt,int rt){
lt=find(root,lt);rt=find(root,rt);
//一定要返回去看,因为这些坐标是提前对应好的
splay(lt,0);splay(rt,lt);
return ls(rt);
}
inline void delet(int lt,int rt){
int now=divid(lt,rt),nxt=fa(now);
split(now);
ls(nxt)=0;
pushup(nxt);pushup(root);
}
inline void modify(int lt,int rt,int val){
int now=divid(lt,rt),nxt=fa(now);
mdf(now)=1;val(now)=val;
num(now)=siz(now)*val(now);
if(val>0)mx(now)=lmx(now)=rmx(now)=num(now);
else mx(now)=val,lmx(now)=rmx(now)=0;
pushup(nxt);pushup(root);
}
inline void reverse(int lt,int rt){
int now=divid(lt,rt),nxt=fa(now);
if(!mdf(now)){
lzy(now)^=1;
swap(ls(now),rs(now));
swap(lmx(now),rmx(now));
pushup(nxt);pushup(root);
}
}
inline int getsum(int lt,int rt){
int now=divid(lt,rt);
return num(now);
}
inline int getmxs(int lt,int rt){
lt=find(root,lt);rt=find(root,rt);
//一定要返回去看,因为这些坐标是提前对应好的
splay(lt,0);splay(rt,lt);
return mx(ls(rt));
}
inline int getnum(int lt){
lt=find(root,lt+1);
return val(lt);
}
int main(){
n=read();m=read();
for(int i=2;i<=n+1;++i){
a[i]=read();id[i]=i;
}
mx(0)=a[1]=a[n+2]=-INF;id[1]=1;id[n+2]=n+2;
//因为不能存0,所以集体右移一位了
build(1,n+2,0);cnt=n+2;root=(cnt+1)>>1;
while(m--){
scanf("%s",s);
if(s[0]=='I'){
int posi=read(),tot=read();
insert(posi,tot);
}
else if(s[0]=='D'){
int posi=read(),tot=read();
delet(posi,posi+tot+1);
}
else if(s[2]=='K'){
int posi=read(),tot=read(),c=read();
modify(posi,posi+tot+1,c);
}
else if(s[0]=='R'){
int posi=read(),tot=read();
reverse(posi,posi+tot+1);
}
else if(s[0]=='G'&&s[3]=='-'){
int posi=read(),tot=read();
printf("%d\n",getsum(posi,posi+tot+1));
}
else if(s[0]=='M'&&s[3]=='-'){
int posi=read(),tot=read();
printf("%d\n",getmxs(posi,posi+tot+1));
}
else {
int posi=read();
printf("%d\n",getnum(posi));
}
}
return 0;
}
忠告:做这题的时候,佛系一点总不会错(为什么放最后呀。。)
luogu P2710 数列的更多相关文章
- luogu 1327 数列排序 & 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛 J题 循环节
luogu 1327 数列排序 题意 给定一个数列\(\{an\}\),这个数列满足\(ai≠aj(i≠j)\),现在要求你把这个数列从小到大排序,每次允许你交换其中任意一对数,请问最少需要几次交换? ...
- Luogu P1062 数列
Luogu P1062 数列 题目说: 把所有$k$的方幂及所有有限个互不相等的$k$的方幂之和构成一个递增的序列. 这就是说,每一个$k$的方幂只能有或没有. 有为$0$,没有为$1$. 所以这些数 ...
- luogu P1182 数列分段Section II
题目描述 对于给定的一个长度为N的正整数数列A[i],现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小. 关于最大值最小: 例如一数列4 2 4 5 1要分成3段 将其如下分段: [4 ...
- Luogu P3901 数列找不同
由于技术原因,题目我贴不上了,大家点下面的链接自己去看吧^_^ P3901 数列找不同 这题第一眼看去,题面真短,有坑(flag) 在往下面看去,woc数据这么大,你要怎样. 现在一起想想想,超级侦探 ...
- luogu P1356 数列的整数性 |动态规划
题目描述 对于任意一个整数数列,我们可以在每两个整数中间任意放一个符号'+'或'-',这样就可以构成一个表达式,也就可以计算出表达式的值.比如,现在有一个整数数列:17,5,-2,-15,那么就可以构 ...
- luogu P1327 数列排序
题目描述 给定一个数列{an},这个数列满足ai≠aj(i≠j),现在要求你把这个数列从小到大排序,每次允许你交换其中任意一对数,请问最少需要几次交换? 输入格式 第一行,正整数n (n<=10 ...
- luogu P1181 数列分段Section I x
P1181 数列分段Section I 题目描述 对于给定的一个长度为N的正整数数列A[i],现要将其分成连续的若干段,并且每段和不超过M(可以等于M),问最少能将其分成多少段使得满足要求. 输入输出 ...
- luogu P4948 数列求和 推式子 简单数学推导 二项式 拉格朗日插值
LINK:数列求和 每次遇到这种题目都不太会写.但是做法很简单. 终有一天我会成功的. 考虑类似等比数列求和的东西 帽子戏法一下. 设\(f(k)=\sum_{i=1}^ni^ka^i\) 考虑\(a ...
- 【题解】Luogu P3901 数列找不同
我博客中对莫队的详细介绍 原题传送门 不错的莫队练手题 块数就直接取sqrt(n) 对所有询问进行排序 排序第一关键词:l所在第几块,第二关键词:r的位置 考虑Ai不大,暴力开数组 add时如果加之后 ...
随机推荐
- K8s - Kubernetes重要概念介绍(Cluster、Master、Node、Pod、Controller、Service、Namespace)
K8s - Kubernetes重要概念介绍(Cluster.Master.Node.Pod.Controller.Service.Namespace) Kubernetes 是目前发展最 ...
- GO语言面向对象02---继承
package main import ( "fmt" ) type Dog struct { Name string Age int } func (d *Dog)bite() ...
- Go语言流程控制05--defer延时执行
package main import "fmt" func xingzuoZhensuo() { var birthday string fmt.Println("请输 ...
- 如何使用TVM Pass红外线
如何使用TVM Pass红外线 随着Relay / tir中优化遍数的增加,执行并手动维护其依赖关系变得很棘手.引入了一个基础结构来管理优化过程,将其应用于TVM堆栈中IR的不同层. Relay / ...
- 中国摄像头CMOS需求潜力旺盛
中国摄像头CMOS需求潜力旺盛 CMOS是Complementary Metal Oxide Semiconductor(互补金属氧化物半导体)的缩写.它是指制造大规模集成电路芯片用的一种技术或用这种 ...
- jmeter链接mysql数据库
一.下载与MySQL对应的jar包 1.1.查询MySQL的版本, 命令语句 :SELECT VERSION(); 1.2.MySQL官网下载jar包 ,https://downloads.mysql ...
- 从一道高大上的面试题来学习位图算法BitMap
今天我偶然刷到了一篇文章,"华为二面:一个文件里面有5亿个数据,一行一个,没有重复的,进行排序".不知道又是哪个无良媒体瞎起的标题,夺人眼球. 不过说归说,这题听着就很高大上,5亿 ...
- 【NX二次开发】Block UI 面收集器
属性说明 属性 类型 描述 常规 BlockID String 控件ID Enable Logical 是否可操作 Group ...
- Java IO学习笔记八:Netty入门
作者:Grey 原文地址:Java IO学习笔记八:Netty入门 多路复用多线程方式还是有点麻烦,Netty帮我们做了封装,大大简化了编码的复杂度,接下来熟悉一下netty的基本使用. Netty+ ...
- 【模拟8.01】string(线段树)
因为题中只有a-z,所以区间中大量字母都是重复的,我们不妨利用桶的性质. 开一棵树,里面维护当前区间内的相同元素,若区间内元素不同,则为零 每次升序操作就先查询一遍区间,用桶将每个区间的a-z元素统计 ...