[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: 历史研究(回滚莫队)
传送门 这是一个叫做回滚莫队的神奇玩意儿 是询问,而且不强制在线,就决定是你了莫队 如果是每次插入一个数是不是很简单? 然而悲剧的是我们莫队的时候不仅要插入数字还要删除数字 那么把它变成只插入不就行了 ...
随机推荐
- USB-TTL
- [CF1051F] Shortest Statement
问题描述 You are given a weighed undirected connected graph, consisting of n vertices and m edges. You s ...
- C# 基础:DataTable操作、发邮件
本文出自:https://www.cnblogs.com/2186009311CFF/p/6865909.html DataTable操作 据参数删除为0的列:包括遍历.删除.取值 public st ...
- asp.net (web)选择文件夹 上传文件
1 背景 用户本地有一份txt或者csv文件,无论是从业务数据库导出.还是其他途径获取,当需要使用蚂蚁的大数据分析工具进行数据加工.挖掘和共创应用的时候,首先要将本地文件上传至ODPS,普通的小文件通 ...
- 求大师点化,寻求大文件(最大20G左右)上传方案
之前仿造uploadify写了一个HTML5版的文件上传插件,没看过的朋友可以点此先看一下~得到了不少朋友的好评,我自己也用在了项目中,不论是用户头像上传,还是各种媒体文件的上传,以及各种个性的业务需 ...
- BZOJ 3319: 黑白树 并查集 + 离线 + 思维
Description 给定一棵树,边的颜色为黑或白,初始时全部为白色.维护两个操作: 1.查询u到根路径上的第一条黑色边的标号. 2.将u到v 路径上的所有边的颜色设为黑色. Notice:这 ...
- 字符串截取模板 && POJ 3450、3080 ( 暴力枚举子串 && KMP匹配 )
//截取字符串 ch 的 st~en 这一段子串返回子串的首地址 //注意用完需要根据需要最后free()掉 char* substring(char* ch,int st,int en) { ; c ...
- SQL 查询表字段长度, 名称, 类型, 存储过程创建和修改时间
获取存储过程的修改时间和创建时间查询建立时间 --表 select * from sysobjects where id=object_id(N'表名') and xtype='U' --表的结构 s ...
- [CF535D]Tavas and Malekas 题解
题意简述 有一个空着的长度为\(n\)的字符串\(ans\),再给出一个长度为\(m\)的序列\(a\),现要在序列中每个元素\(a_i\)的位置开始都规定必须为一个给定的字符串\(s\).问字符串\ ...
- echarts画环形图
alarmManage(){ let drawLine = echarts.init(document.getElementById('data-alarmManage-table-wrap')); ...