[BZOJ4358]Permu(回滚莫队)

题面

给出一个长度为n的排列P(P1,P2,...Pn),以及m个询问。每次询问某个区间[l,r]中,最长的值域连续段长度。

分析

最简单的方法显然是用线段树维护最长值域连续段长度,复杂度\(O(n\sqrt n \log n)\),会TLE

我们以值为下标维护两个数组lb[v],rb[v]表示<v(定义为“左侧”)和>v(定义为“右侧)的连续段长度,当我们加入一个值v的时候,会产生一个长度为lb[v]+rb[v]+1的连续段,,可以用来更新答案。同时,我们需要更新lb,rb。对于v两端的连续段来说,我们不需要修改每一个值的lb,rb,只需要修改段边界的就可以了,因为下一次插入值w的时候,只有w落在某个连续段的边界上,我们才要更新答案,而更新的答案只与段边界的lb,rb有关

然后考虑如何莫队处理:

我们把询问以左端点块编号为第一关键字,右端点为第二关键字排序。对于一个块中的所有询问,我们发现r是递增的,所以不用撤销。而l不一定是递增的,我们要考虑如何撤销某个添加值的操作。

对于每一块,初始r设为块的右端点,然后每个询问先将r往右移,处理r和l不在同一块的询问。然后处理询问在块内之间的部分,直接暴力把询问的块内部分的所有点添加进去。修改前把原来lb,rb的值记下来,保存在栈里面。处理完这个询问之后再复原。

我们发现这样的时间复杂度和普通莫队是相同的,因为加入和撤销相当于左端点从l1移到l2,再移回来,因为在同一个块内,所以移动距离不会超过\(O(\sqrt n)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 50000
#define maxm 50000
using namespace std;
inline void qread(int &x) {
x=0;
int sign=1;
char c=getchar();
while(c<'0'||c>'9') {
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
x=x*sign;
}
inline void qprint(int x) {
if(x<0) {
putchar('-');
qprint(-x);
} else if(x==0) {
putchar('0');
return;
} else {
if(x>=10) qprint(x/10);
putchar('0'+x%10);
}
}
int n,m;
int a[maxn+5];
int bsz;
int belong[maxn+5];
struct query{
int l;
int bel;//减少数组寻址次数
int r;
int id;
friend bool operator < (query p,query q){
return p.bel<q.bel||(p.bel==q.bel&&p.r<q.r);
}
}q[maxm+5]; int ans[maxn+5];
int rb[maxn+5],lb[maxn+5];
//记录第i向左和向右的连续段长度 //用于撤销修改操作的栈
struct node{
int type;//记录是哪个数组修改,1则是lb,2则是rb
int pos;
int val;
node(){ }
node(int _type,int _pos,int _val){
type=_type;
pos=_pos;
val=_val;
}
}st[maxn+5];
int main(){
qread(n);
qread(m);
for(int i=1;i<=n;i++) qread(a[i]);
bsz=n/sqrt(m);
for(int i=1;i<=n;i++) belong[i]=i/bsz+1;
for(int i=1;i<=m;i++){
qread(q[i].l);
qread(q[i].r);
q[i].id=i;
q[i].bel=belong[q[i].l];
}
sort(q+1,q+1+m);
int r=0;
int sum=0;
for(int i=1;i<=m;i++){
if(q[i].bel!=q[i-1].bel){//新的块
sum=0;
for(int i=1;i<=n;i++) lb[i]=rb[i]=0;
r=q[i].bel*bsz;
}
while(r<q[i].r){//如果l,r不在同一个块,把r在块外部分加入,r单调递增,可不用撤销修改
r++;
lb[a[r]]=lb[a[r]-1]+1;
rb[a[r]]=rb[a[r]+1]+1;
int tmp=lb[a[r]]+rb[a[r]]-1;
sum=max(sum,tmp);
//对于a[r]两端的连续段来说,我们不需要修改每一个值的lb,rb,只需要修改段边界的就可以了
//因为下一次插入a[r],若a[r]已经存在,则答案不变,
//若a[r]落在某个连续段的边界上,我们才要更新答案,而更新的答案只与段边界的lb,rb有关
lb[a[r]+rb[a[r]]-1]=tmp;
rb[a[r]-lb[a[r]]+1]=tmp;
}
int res=sum;//由于撤销对sum的修改比较麻烦,移动左端点的时候不更新sum,而更新答案res
int top=0;
//min(q[i].r,q[i].bel*bsz)表示把询问在当前块内部分加入
for(int l=q[i].l;l<=min(q[i].r,q[i].bel*bsz);l++){//移动左端点,要回滚
lb[a[l]]=lb[a[l]-1]+1;
rb[a[l]]=rb[a[l]+1]+1;
int tmp=lb[a[l]]+rb[a[l]]-1;
st[++top]=node(1,a[l]+rb[a[l]]-1,lb[a[l]+rb[a[l]]-1]);//修改前把原来的值记下来
st[++top]=node(2,a[l]-lb[a[l]]+1,rb[a[l]-lb[a[l]]+1]);
lb[a[l]+rb[a[l]]-1]=tmp;
rb[a[l]-lb[a[l]]+1]=tmp;
res=max(res,tmp);
}
for(int j=top;j>=1;j--){//撤销对连续段端点的修改
if(st[j].type==1) lb[st[j].pos]=st[j].val;
else rb[st[j].pos]=st[j].val;
}
for(int j=q[i].l;j<=min(q[i].r,q[i].bel*bsz);j++){//撤销新加入的点对lb,rb的修改
lb[a[j]]=rb[a[j]]=0;
}
ans[q[i].id]=res;
}
for(int i=1;i<=m;i++){
qprint(ans[i]);
putchar('\n');
}
}

[BZOJ4358]Permu(回滚莫队)的更多相关文章

  1. [bzoj4358]permu:莫队+线段树/回滚莫队

    这道题是几天前水过去的,现在快没印象了,水一发. 首先我们看到它让求解的是最长的值域 连续段长度,很好. 然后就想到了山海经,但但是我还没有做. 然后又想到了很久以前的一次考试的T3旅馆hotel(我 ...

  2. [CSP-S模拟测试]:ants(回滚莫队)

    题目描述 然而贪玩的$dirty$又开始了他的第三个游戏. $dirty$抓来了$n$只蚂蚁,并且赋予每只蚂蚁不同的编号,编号从$1$到$n$.最开始,它们按某个顺序排成一列.现在$dirty$想要进 ...

  3. LOJ.6504.[雅礼集训2018 Day5]Convex(回滚莫队)

    LOJ 莫队.发现只需要维护前驱后继就可以了. 但是加入一个点需要找到它当前的前驱后继,很麻烦还带个\(\log\). 但是如果只有删除某个点,只需要更新一下它的前驱后继即可. 用回滚莫队就好惹. 撤 ...

  4. BZOJ.4241.历史研究(回滚莫队 分块)

    题目链接 \(Description\) 长度为n的数列,m次询问,每次询问一段区间最大的 \(A_i*tm_i\) (重要度*出现次数) \(Solution\) 好像可以用莫队做,但是取max的操 ...

  5. 2018.09.26 bzoj5218: [Lydsy2017省队十连测]友好城市(回滚莫队)

    传送门 比较简单的一道回滚莫队吧. 每次询问用bitset优化kosaraju统计答案. 就是有点难调. 然后向dzyo学长学习了回滚莫队的一种简洁的实现方式,就是直接建立一个sqrt(m)∗sqrt ...

  6. 2018.08.14 bzoj4241: 历史研究(回滚莫队)

    传送们 简单的回滚莫队,调了半天发现排序的时候把m达成了n... 代码: #include<bits/stdc++.h> #define N 100005 #define ll long ...

  7. BZOJ4241:历史研究(回滚莫队)

    Description IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. ...

  8. LOJ#6504. 「雅礼集训 2018 Day5」Convex(回滚莫队)

    题面 传送门 题解 因为并不强制在线,我们可以考虑莫队 然而莫队的时候有个问题,删除很简单,除去它和前驱后继的贡献即可.但是插入的话却要找到前驱后继再插入,非常麻烦 那么我们把它变成只删除的回滚莫队就 ...

  9. bzoj4241: 历史研究(回滚莫队)

    传送门 这是一个叫做回滚莫队的神奇玩意儿 是询问,而且不强制在线,就决定是你了莫队 如果是每次插入一个数是不是很简单? 然而悲剧的是我们莫队的时候不仅要插入数字还要删除数字 那么把它变成只插入不就行了 ...

随机推荐

  1. 小程序内置组件swiper,circular(衔接)使用小技巧

    swiper,关于滑块的一些效果无缝,断点,视差等等...我想这里就不用做太多的赘述,这里给大家分享一下实战项目中使用circular(衔接)的一点小特性.小技巧,当然你也可以理解为遇到了一个小坑,因 ...

  2. 前端之CSS:CSS选择器

    前端之css样式(选择器)... 一.css概述 CSS是Cascading Style Sheets的简称,中文称为层叠样式表,对html标签的渲染和布局 CSS 规则由两个主要的部分构成:选择器, ...

  3. SpringBoot实体类对象和json格式的转化

    1.引入maven依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson ...

  4. Oracle12c RAC RMAN异机恢复

    ######################################################## #编辑pfile文件initspdb.ora vi /oracle/app/oracl ...

  5. 【转载】MIMO技术杂谈(一):鱼与熊掌能否兼得?--浅谈分集与复用的权衡

    原文链接(向作者致敬):http://www.txrjy.com/thread-667901-1-1.html   无线通信世界在过去的几十年中的发展简直是爆发式的,MIMO(多发多收)技术的出现更是 ...

  6. linux运维、架构之路-LVS负载均衡

    一.LVS介绍 1.介绍           LVS是Linux Virtual Server的简写,是linux虚拟的服务器集群系统,可以在unix/linux平台下实现负载均衡集群功能,由章文嵩博 ...

  7. React Native 之FlatList 下拉刷新和上拉加载更多

    接上一篇代码: 只修改了FlatListDemo.js里面的代码 import React, {Fragment,Component} from 'react'; import { SafeAreaV ...

  8. 使用穷举法结合numpy解决八皇后问题

    一般说到八皇后问题,最先想到的就是回溯思想,而回溯思想往往是需要递归来实现的. 计算机很善长做重复的事情,所以递归正和它的胃口,而我们人脑更喜观平铺直叙的思维方式.当 我们看到递归时,总想把递归平铺展 ...

  9. JS中的执行机制(setTimeout、setInterval、promise、宏任务、微任务)

    1.执行机制 JS 是单线程的,处理 JS 任务(程序)只能一个一个顺序执行,所以 JS 中就把任务分为了同步任务和异步任务.同步的进入主线程先执行,异步的进入Event Table并注册函数,当指定 ...

  10. CF786B Legacy 线段树优化建图 + spfa

    CodeForces 786B Rick和他的同事们做出了一种新的带放射性的婴儿食品(???根据图片和原文的确如此...),与此同时很多坏人正追赶着他们.因此Rick想在坏人们捉到他之前把他的遗产留给 ...