CF803G Periodic RMQ Problem
简要题意
你需要维护一个序列 \(a\),有 \(q\) 个操作,支持:
1 l r x将 \([l,r]\) 赋值为 \(x\)。2 l r询问 \([l,r]\) 的最小值。
为了加大难度,\(a\) 不会直接告诉你,但是会告诉你一个长度为 \(n\) 的序列 \(b\) 和一个整数 \(k\),\(a\) 是将 \(b\) 复制 \(k\) 遍依次拼接而成。
\(1 \leq n,q \leq 10^{5},1 \leq k \leq 10^{4},1 \leq b_i \leq 10^9,1 \leq l_i \leq r_i \leq nk\)
思路
如果直接求出 \(a\),那么时间复杂度和空间复杂度(都是 \(O(nk)\))不能接受。
我发现,如果没有修改,那么只需要用一个 ST 表即可,具体操作如下:
- 如果 \((r-l+1)\geq n\),那么一定包含所有 \(b\) 中的元素,直接返回 \(b[1,n]\) 的最小值。
- 如果 \((l-1)\bmod n+1>(r-1)\bmod n+1\),那么这个区间是跨区块的,如图:

(红色为 \(b\) 块,蓝色为询问 \([l,r]\),黑色为数值)
那么就是相对应的后缀最小值和前缀最小值的最小值。
- 剩下的情况是完全在 \(b\) 块中,ST 表查询。
如果带上修改呢?我们可以打一个标记,如果这个区间有修改,那么直接读取修改后的结果,否则走上述过程。这一步可以使用动态开点线段树完成。
不过我的动态开点线段树总是会 TLE 掉一些点,由于本题数据随机,我们使用 ODT 替换掉动态开点线段树,就可以 AC 了。
时间复杂度均摊 \(O(n\log n+q\log nk)\),可以通过本题。
代码
#include<bits/stdc++.h>
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
using namespace std;
const int N=100005;
int n,m,k,f[N][25],lg[N]= {-1},pre[N],suf[N];
int min_=INT_MAX,min_ans=INT_MAX;
int st(int l,int r){
int logg=lg[r-l+1];
return min(f[l][logg],f[r-(1<<logg)+1][logg]);
}
int solve(int l,int r) {
if(r-l+1>=n){// 如果包含整个块,那么最小值一定是 min(b[1],...,b[n])
return min_;
}
l=(l-1)%n+1; // 转成块内坐标
r=(r-1)%n+1;// 转成块内坐标
if(l>r){// 如果不在同一个块,直接返回右半块+左半块
return min(pre[r],suf[l]);
}
return st(l,r); // 如果在同一个块,ST表查询
}
struct node {
int l,r;
mutable int v;
bool covered;
node(int ls,int rs=-1,int vv=0,bool coverevd=0){
l=ls,r=rs,v=vv,covered=coverevd;
}
bool operator<(const node &a)const {
return this->l<a.l;
}
};
set<node>s;
set<node>::iterator split(int p) {// 分裂区间
if(p>n*k)return s.end();
set<node>::iterator it=--s.upper_bound(node(p,0,0,0));
if((*it).l==p)return it;
int l=(*it).l,r=(*it).r,v=(*it).v;
bool covered=(*it).covered;
s.erase(it);
s.insert(node(l,p-1,v,covered));
return s.insert(node(p,r,v,covered)).first;
}
void assign(int l,int r,int v) { // 区间赋值
set<node>::iterator rs=split(r+1),ls=split(l);
s.erase(ls,rs);
s.insert(node(l,r,v,1));
min_ans=min(min_ans,v);
}
int query(int l,int r) {
int ans=INT_MAX;
set<node>::iterator rs=split(r+1),ls=split(l);
for(set<node>::iterator it=ls; it!=rs; ++it) {
bool covered=(*it).covered;
if(covered) { // 如果被覆盖了,就直接用覆盖的值
ans=min(ans,(*it).v);
} else { // 如果没有被覆盖,就去 ST 表中查询
ans=min(ans,solve((*it).l,(*it).r));
}
if(ans==min_ans)break; // 如果已经查到全序列最小值,就直接返回
}
return ans;
}
int main() {
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) {
scanf("%d",&f[i][0]);
}
pre[0]=suf[n+1]=INT_MAX;
for(int i=1;i<=n;i++) {
pre[i]=min(pre[i-1],f[i][0]);
lg[i]=lg[i>>1]+1;
min_=min(min_,f[i][0]);
min_ans=min(min_ans,f[i][0]);
}
for(int i=n;i>=1;i--) {
suf[i]=min(suf[i+1],f[i][0]);
}
for(int j=1;j<=20;j++) {
for(int i=1;i<=n+1-(1<<j);i++){
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
s.insert(node(1,n*k,min_,0));
scanf("%d",&m);
for(int i=1;i<=m;i++) {
int op,l,r,x;
scanf("%d%d%d",&op,&l,&r);
if(op==1) {
scanf("%d",&x);
assign(l,r,x);
} else {
printf("%d\n",query(l,r));
}
}
return 0;
}
CF803G Periodic RMQ Problem的更多相关文章
- CF803G - Periodic RMQ Problem 动态开点线段树 或 离线
CF 题意 有一个长度为n × k (<=1E9)的数组,有区间修改和区间查询最小值的操作. 思路 由于数组过大,直接做显然不行. 有两种做法,可以用动态开点版本的线段树,或者离线搞(还没搞)( ...
- Codeforces 803G Periodic RMQ Problem 线段树
Periodic RMQ Problem 动态开点线段树直接搞, 我把它分成两部分, 一部分是原来树上的, 一部分是后来染上去的,两个部分取最小值. 感觉有点难写.. #include<bits ...
- codeforces 803G Periodic RMQ Problem
codeforces 803G Periodic RMQ Problem 题意 长度为\(1e5\)的数组复制\(1e4\)次,对新的数组进行区间覆盖和区间最小值查询两种操作,操作次数\(1e5\). ...
- AC日记——Periodic RMQ Problem codeforces 803G
G - Periodic RMQ Problem 思路: 题目给一段序列,然后序列复制很多次: 维护序列很多次后的性质: 线段树动态开点: 来,上代码: #include <cstdio> ...
- (WAWAWAWAWAWAW) G. Periodic RMQ Problem
没有联通门 : Codeforces G. Periodic RMQ Problem /* Codeforces G. Periodic RMQ Problem MMP 什么动态开点线段树啊 ... ...
- Codeforces 803 G. Periodic RMQ Problem
题目链接:http://codeforces.com/problemset/problem/803/G 大致就是线段树动态开节点. 然后考虑到如果一个点还没有出现过,那么这个点显然未被修改,就将这个点 ...
- Codeforces 803G Periodic RMQ Problem ST表+动态开节点线段树
思路: (我也不知道这是不是正解) ST表预处理出来原数列的两点之间的min 再搞一个动态开节点线段树 节点记录ans 和标记 lazy=-1 当前节点的ans可用 lazy=0 没被覆盖过 els ...
- BZOJ 3489: A simple rmq problem
3489: A simple rmq problem Time Limit: 40 Sec Memory Limit: 600 MBSubmit: 1594 Solved: 520[Submit] ...
- BZOJ3339 Rmq Problem
[bzoj3339]Rmq Problem Description Input Output Sample Input 7 5 0 2 1 0 1 3 2 1 3 2 3 1 4 3 6 2 7 Sa ...
- bzoj 3489: A simple rmq problem k-d树思想大暴力
3489: A simple rmq problem Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 551 Solved: 170[Submit][ ...
随机推荐
- 齐博x1客服系统显示客户在哪个页面
如下图所示,要想实现下面的效果,即显示客户给你发消息时,当时处于哪个商品页面.这样方便跟客户针对此商品进行交流. 你的模板如果使用了碎片的话,就可以添加下面的代码index_style/default ...
- python关于Django搭建简单博客项目(教程)
由于csdn各种django blog博文都有或多或少的bug,所以我决定自己写一篇,先附上教程,详解在另一篇博文里,为了便于大家复制粘贴,本文代码尽量不使用图片. 源代码及解析文章请在我的githu ...
- 26.ViewSet和action
在dispatch过程中,下列属性可用于 ViewSet : basename - 根url路径 action - 当前动作类型(例如 list , create ). detail - 用于指示 ...
- 5.github操作
Github设置远程仓库 将我们github的https或者ssh远程仓库地址复制 git remote add https://xxxxxxxTest.git # 指定github仓库设置为远程 ...
- Golang 和Python 几个小时前 几分钟 几天前的处理
在用golang爬虫的时候 总会遇到 10天前 10分钟前 刚刚这种很影响我们爬取正常事件 所以我写了个方法 来格式化这种事件 golang 版本 package utils import ( &qu ...
- 基于SqlSugar的开发框架循序渐进介绍(21)-- 在工作流列表页面中增加一些转义信息的输出,在后端进行内容转换
有时候,为了给前端页面输出内容,有时候我们需要准备和数据库不一样的实体信息,因为数据库可能记录的是一些引用的ID或者特殊字符,那么我们为了避免前端单独的进行转义处理,我们可以在后端进行统一的格式化后再 ...
- for in 和 for of 的区别和v-for指令的三种使用方法
for...in 循环:只能获得对象的键名,不能获得键值 for...of 循环:允许遍历获得键值 var arr = ['red', 'green', 'blue'] for(let item in ...
- Python 学习思路 思维导图 Xmind
如果需要,请在评论区留下邮箱,我看到后会一次发送.
- 2.5:Python常用内置数据结构、多维数组ndarray、Series和DataFrame
一.Python内置数据结构 1.赋值生成列表 la=[1,2,3,4] la 2.强制转换为列表 lb=list("Hello") lb 3.推导式生成列表 s="ab ...
- 【Java SE】课程目录
〇.课程简介 一.前言入门 二.数据类型.运算符.方法 三.流程控制 四.Idea.方法 五.数组 六.类.对象.封装.构造 七.Scanner类.Random类.ArrayList类 八.Strin ...