[BZOJ4358]Permu(回滚莫队)
[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(回滚莫队)的更多相关文章
- [bzoj4358]permu:莫队+线段树/回滚莫队
这道题是几天前水过去的,现在快没印象了,水一发. 首先我们看到它让求解的是最长的值域 连续段长度,很好. 然后就想到了山海经,但但是我还没有做. 然后又想到了很久以前的一次考试的T3旅馆hotel(我 ...
- [CSP-S模拟测试]:ants(回滚莫队)
题目描述 然而贪玩的$dirty$又开始了他的第三个游戏. $dirty$抓来了$n$只蚂蚁,并且赋予每只蚂蚁不同的编号,编号从$1$到$n$.最开始,它们按某个顺序排成一列.现在$dirty$想要进 ...
- LOJ.6504.[雅礼集训2018 Day5]Convex(回滚莫队)
LOJ 莫队.发现只需要维护前驱后继就可以了. 但是加入一个点需要找到它当前的前驱后继,很麻烦还带个\(\log\). 但是如果只有删除某个点,只需要更新一下它的前驱后继即可. 用回滚莫队就好惹. 撤 ...
- BZOJ.4241.历史研究(回滚莫队 分块)
题目链接 \(Description\) 长度为n的数列,m次询问,每次询问一段区间最大的 \(A_i*tm_i\) (重要度*出现次数) \(Solution\) 好像可以用莫队做,但是取max的操 ...
- 2018.09.26 bzoj5218: [Lydsy2017省队十连测]友好城市(回滚莫队)
传送门 比较简单的一道回滚莫队吧. 每次询问用bitset优化kosaraju统计答案. 就是有点难调. 然后向dzyo学长学习了回滚莫队的一种简洁的实现方式,就是直接建立一个sqrt(m)∗sqrt ...
- 2018.08.14 bzoj4241: 历史研究(回滚莫队)
传送们 简单的回滚莫队,调了半天发现排序的时候把m达成了n... 代码: #include<bits/stdc++.h> #define N 100005 #define ll long ...
- BZOJ4241:历史研究(回滚莫队)
Description IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. ...
- LOJ#6504. 「雅礼集训 2018 Day5」Convex(回滚莫队)
题面 传送门 题解 因为并不强制在线,我们可以考虑莫队 然而莫队的时候有个问题,删除很简单,除去它和前驱后继的贡献即可.但是插入的话却要找到前驱后继再插入,非常麻烦 那么我们把它变成只删除的回滚莫队就 ...
- bzoj4241: 历史研究(回滚莫队)
传送门 这是一个叫做回滚莫队的神奇玩意儿 是询问,而且不强制在线,就决定是你了莫队 如果是每次插入一个数是不是很简单? 然而悲剧的是我们莫队的时候不仅要插入数字还要删除数字 那么把它变成只插入不就行了 ...
随机推荐
- Python---面向对象---龟鱼游戏
一.定义一个门票系统 门票的原价是100元 当周末的时候门票涨价20% 小孩子半票 计算2个成人和1个小孩的平日票价 ----------------------------------------- ...
- .Net 网站配置文件 webconfig 配置。 字体图标+视频播放 以及 文件上传
ASP.NET MVC 上传大文件时404 原来IIS7的上传文件大小,即便是在经典模式下,也一定要在system.webServer里设置,加上去就OK了 <system.webServer& ...
- IDEA unable to find valid certification path to requested target
一.报错 Could not transfer artifact org.apache.maven.plugins:maven-install-plugin:pom:2.4 from/to alima ...
- LTE抛弃了CDMA?
原文链接:https://blog.csdn.net/readhere/article/details/82764919 本文节选自<LTE教程:结构与实施> 大家都听说过这样的说法:LT ...
- CF1244C
题目描述 给出n,p,w,d,求(x,y,z)使得 xw+yd=p x+y+z=n 其中d<w<10^5^ 题解 显然扩欧啊( 来自天国的long long y如果大于等于w,则显然可以把 ...
- 软件工程 in MSRA Code Search-第二次结对编程
重现基线模型 我们选择了 code2vec 模型进行复现.该模型由 Uri Alon 等作者于 2018 年提出. 模型思路: 从代码与普通语言相比的特殊性入手,首先,对于输入的代码段,作者考虑到尽管 ...
- 【PowerOJ1752&网络流24题】运输问题(费用流)
题意: 思路: [问题分析] 费用流问题. [建模方法] 把所有仓库看做二分图中顶点Xi,所有零售商店看做二分图中顶点Yi,建立附加源S汇T. 1.从S向每个Xi连一条容量为仓库中货物数量ai,费用为 ...
- [ethereum源码分析](5) 创建新账号
前言 在上一章我们介绍了 ethereum运行开启console 的过程,那么在这一章我们将会介绍如何在以太坊中创建一个新的账号.以下的理解可能存在错误,如果各位大虾发现错误,还望指正. 指令分析 指 ...
- [BZOJ2038]:[2009国家集训队]小Z的袜子(hose)(离线莫队)
题目传送门 题目描述 作为一个生活散漫的人,小$Z$每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……具体来说,小$Z$把这 ...
- be of +名词 = 形容词 (词性变化) ; || it is adj. of + 人称宾格 = 人称主格 + 形容词 (人称变化)
be of +名词 = 形容词 这是一种常用的构词法 of + 名词 就等于 对应的形容词, 这也是, 扩展词汇的一种方法. 原则上你可以任意使用, 但是, 通常只是针对 那些形容词, 名词相对来说比 ...