简要题意

你需要维护一个序列 \(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的更多相关文章

  1. CF803G - Periodic RMQ Problem 动态开点线段树 或 离线

    CF 题意 有一个长度为n × k (<=1E9)的数组,有区间修改和区间查询最小值的操作. 思路 由于数组过大,直接做显然不行. 有两种做法,可以用动态开点版本的线段树,或者离线搞(还没搞)( ...

  2. Codeforces 803G Periodic RMQ Problem 线段树

    Periodic RMQ Problem 动态开点线段树直接搞, 我把它分成两部分, 一部分是原来树上的, 一部分是后来染上去的,两个部分取最小值. 感觉有点难写.. #include<bits ...

  3. codeforces 803G Periodic RMQ Problem

    codeforces 803G Periodic RMQ Problem 题意 长度为\(1e5\)的数组复制\(1e4\)次,对新的数组进行区间覆盖和区间最小值查询两种操作,操作次数\(1e5\). ...

  4. AC日记——Periodic RMQ Problem codeforces 803G

    G - Periodic RMQ Problem 思路: 题目给一段序列,然后序列复制很多次: 维护序列很多次后的性质: 线段树动态开点: 来,上代码: #include <cstdio> ...

  5. (WAWAWAWAWAWAW) G. Periodic RMQ Problem

    没有联通门 : Codeforces G. Periodic RMQ Problem /* Codeforces G. Periodic RMQ Problem MMP 什么动态开点线段树啊 ... ...

  6. Codeforces 803 G. Periodic RMQ Problem

    题目链接:http://codeforces.com/problemset/problem/803/G 大致就是线段树动态开节点. 然后考虑到如果一个点还没有出现过,那么这个点显然未被修改,就将这个点 ...

  7. Codeforces 803G Periodic RMQ Problem ST表+动态开节点线段树

    思路: (我也不知道这是不是正解) ST表预处理出来原数列的两点之间的min 再搞一个动态开节点线段树 节点记录ans 和标记 lazy=-1 当前节点的ans可用  lazy=0 没被覆盖过 els ...

  8. BZOJ 3489: A simple rmq problem

    3489: A simple rmq problem Time Limit: 40 Sec  Memory Limit: 600 MBSubmit: 1594  Solved: 520[Submit] ...

  9. 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 ...

  10. 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][ ...

随机推荐

  1. NOI2017蚯蚓排队

    原题链接 发现 k<=50 ,在插入和删除时最多会影响不超过 k2 个串,用链表实现插入和删除,然后只需用哈希表维护每个长度不超过k的串的出现次数,哈希的话可以先用比较大的范围的值处理冲突,再映 ...

  2. Golang-Gin Response 统一返回restful格式的数据

    目的: gin返回restful格式的数据,返回的200,201 的数据 也包括异常时的404/500等情况 直接调用即可 package response import ( "github ...

  3. Python基础之面向对象:3、继承与派生

    面向对象 一.三大特征之继承 python三大特征: 封装.继承.多态 三者中继承最为核心,实际应用对,感受较为直观 封装和多态略微抽象 1.继承的概念 继承的含义: ​ 在现实生活中,继承表示人与人 ...

  4. Python基础之模块:6、hashlib模块 subprocess模块 logging模块

    目录 一.hashlib模块 1.简介 2.基本操作与用法 二.subprocess模块 1.简介 2.基本操作与用法 三.logging模块 1.简介 2.基本操作与用法 一.hashlib模块 1 ...

  5. 使用LabVIEW实现基于pytorch的DeepLabv3图像语义分割

    前言 今天我们一起来看一下如何使用LabVIEW实现语义分割. 一.什么是语义分割 图像语义分割(semantic segmentation),从字面意思上理解就是让计算机根据图像的语义来进行分割,例 ...

  6. VM虚拟机搭建Linux CentOS7(手把手教程)

    VM虚拟机搭建Linux CentOS7(手把手教程) 目录 VM虚拟机搭建Linux CentOS7(手把手教程) 一.VM虚拟机和Linux镜像文件下载 1. 登录VM虚拟机官方地址: 2. 安装 ...

  7. Oracle数据泵导入dmp文件,报ORA-39083、ORA-01917错误解决办法

    将10.16.23.111数据库服务器中的数据库名称为cwy_init1的数据导出,随后在10.16.23.112数据库服务器创建空库cwy_init2,将导出的cwy_init1数据文件(cwy_i ...

  8. 2021-2022 ICPC, NERC, Northern Eurasia Onsite (Unrated, Online Mirror, ICPC Rules, Teams Preferred) J. Job Lookup

    题意 n个节点,n<=200,你需要构造这n个几点成为一棵树,并且这棵树的中序遍历为1-n; 你构造树的节点之间的最短路构成一个n×n的最短距离矩阵d: 同时给你n×n的权重矩阵c:最最小的Σd ...

  9. 嵌入式-C语言基础:字符串结束标识符

    #include<stdio.h> int main() { char cdata[]={'h','e','l','l','o'}; char cdata2[]="hello&q ...

  10. python列表、元祖

    #列表创建1 a=['李刚','李白','王维','苏轼','李商隐','苏洵','王安石','司马光'] #列表创建2 b=list(['李刚','李白','王维','苏轼','李商隐','苏洵', ...