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时如果加之后 ...
随机推荐
- Python发送SMTP邮件指南
SMTP(Simple Mail Transfer Protocol)简单邮件传输协议,Python内置对SMTP的支持,可以发送纯文本文件,HTML邮件以及附带文件. 一.两个模块 Pyth ...
- 电脑华硕A455L系列,机械硬盘换成固态硬盘,光驱位改放机械硬盘
电脑一直以来都很卡,直到最近用的不舒服了,就去查查原因,发现是磁盘读取爆表!只好想起来换固态硬盘,光驱改放机械硬盘... 买的固态硬盘是个杂牌军,但是店家说工艺不错,只好将信将疑,用用再说吧. 首先确 ...
- THINKPHP_(4)_TP模型中with、withJoin和多层关联的深入分析
1.个人之前博文: TP模型的多表关联查询和多表字段的关键字搜索 TP6中实现多层关联,第一个表关联第二个表查询出的数据,再关联第三个表 2.withJoin的特性 2.1 第一个特性 在TP模型的多 ...
- Paddle Lite端侧部署
Paddle Lite端侧部署 端侧推理引擎的由来 随着深度学习的快速发展.特别是小型网络模型的不断成熟,原本应用到云端的深度学习推理,就可以放到终端上来做,比如手机.手表.摄像头.传感器.音响,也就 ...
- CVPR2020:点云三维目标跟踪的点对盒网络(P2B)
CVPR2020:点云三维目标跟踪的点对盒网络(P2B) P2B: Point-to-Box Network for 3D Object Tracking in Point Clouds 代码:htt ...
- git操作分支的常用的命令
查看所有的分支: git branch -a 查看当前所在的分支: git branch 检出分支: ...
- 【NX二次开发】根据视图名称旋转视图,在布局中替换视图uc6464
uc6464("布局名","旧视图名","新视图名");输入布局名.旧视图名.新视图名.如果布局名为空则更新当前布局.如果旧视图名为空,则工 ...
- NOIP模拟测试14「旋转子段·走格子·柱状图」
旋转子段 连60分都没想,考试一直肝t3,t2,没想到t1最简单 我一直以为t1很难,看了题解发现也就那样 题解 性质1 一个包含a[i]旋转区间值域范围最多为min(a[i],i)----max(a ...
- Spring Data JPA的Audit功能,审计数据库的变更
我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 数据库审计 数据库审计是指当数据库有记录变更时,可以记录数据库的变更时间和变更人等,这样以后出问题回溯问责也比较方便. ...
- 最佳实践丨使用Rancher轻松管理上万资源不是梦!
前 言 Rancher 作为一个开源的企业级 Kubernetes 集群管理平台.你可以导入现有集群,如 ACK.TKE.EKS.GKE,或者使用 RKE.RKE2.K3s 自定义部署集群. 作为业界 ...