CodeForces 802G Periodic RMQ Problem(线段树+分块思想)
来源:互联网 发布:印度最新人口数据 编辑:程序博客网 时间:2024/06/06 08:45
You are given an array a consisting of positive integers andq queries to this array. There are two types of queries:
- 1 l r x — for each index i such that l ≤ i ≤ r setai = x.
- 2 l r — find the minimum among such ai thatl ≤ i ≤ r.
We decided that this problem is too easy. So the array a is given in a compressed form: there is an array b consisting of n elements and a numberk in the input, and before all queriesa is equal to the concatenation of k arraysb (so the size of a isn·k).
The first line contains two integers n andk (1 ≤ n ≤ 105,1 ≤ k ≤ 104).
The second line contains n integers — elements of the arrayb (1 ≤ bi ≤ 109).
The third line contains one integer q (1 ≤ q ≤ 105).
Then q lines follow, each representing a query. Each query is given either as1l rx — set all elements in the segment froml tillr (including borders) tox (1 ≤ l ≤ r ≤ n·k,1 ≤ x ≤ 109) or as2 l r — find the minimum among all elements in the segment from l till r (1 ≤ l ≤ r ≤ n·k).
For each query of type 2 print the answer to this query — the minimum on the corresponding segment.
3 11 2 332 1 31 1 2 42 1 3
13
3 21 2 352 4 41 4 4 52 4 41 1 6 12 6 6
151
Educationnal CodeForces Round 20 G
果然是Educational,确实可以学到好多东西。
两种操作,区间赋值和询问区间最小,但是总区间是由k个长度为n的相同区间组成,如果直接用普通的线段树去做,很显然在空间上存不下,在时间上也无法接受。所以,很自然的要找一些规律。
由于昨天刚好是抱着想练练分块水的目的取找题的,没想到恰好找到了一个利用分块思路的数据结构题。观察题意很容易发现,本题已经是帮你分好了块的(总共有n块,每一块长度为k),天造地设,正好给我们利用。但是,虽然如此,在时空上,我们还是不能接受……于是又回想到了上一次多校赛的那个我给出加强版做法的那题,对区间进行了离散化,同样,这题也可以这么做,但是要做一些机智的变通。
首先,我们关心的只是区间里面的最小值,所以说,对于每一个区间,我们要保存的有用信息只有其最小值。其次,有改动的区间很少,仅仅只是它输入进来的区间。因此我们可以保存所有的区间端点,对其进行离散化,所有这些端点之间的区域,是需要关注的区域。然后,对于两个相邻端点之间的区域,我们又只需要保存对应区间的最小值,即一个点的坐标。如此一来,假设最后总共有n个端点,那么要保存的数字就只有n-1个,即我们只需要维护这n-1个数字的最小值。对这n-1个数字建立线段树维护即可。
对于每一个操作区间,先找到其端点离散化之后的坐标,在以这个坐标为区间端点对线段树进行操作。可能你会问,仅仅保存区间的最小值不会漏掉什么吗?其实不会的,举一个简单的例子,有一个操作是求区间[l,r]的最小值,你可能会想,如果只保存区间的最小值,如果区间的一部分被修改了怎么办。其实如果有操作修改区间的一部分,那么这个部分的端点必然也会被保存,然后原本的大区间会被分成几个小区间,即所有的区间都被尽量的分小,因此不会出现漏掉信息的情况。
此外,我们之前提到了,在线段树中只保存离散前区间的最小值,那么这个最小值怎么求呢?因为原区间很大,所以必须得用分块。我们同样也可以把这个看成一个询问,对于询问[l,r]不外乎三种情况:1、l和r在同一个块里面(在同一个长度为k的区间里面),这个时候就相当于在区间k里面求[(l-1)%k+1,(r-1)%k+1]的区间最小;2、l和r在相邻的两个块里面(l和r分在两个相邻的长度为k的区间里面),这个时候,在区间k里面求[(l-1)%k+1,k]和[1,(r-1)%k+1],取二者中的小的那个就是答案。3、l和r相距至少一个整块,这是的最小就是整个区间[1,k]的最小值。可以看出,对于这三种情况,我们同样也可以用一棵线段树来完成。但是实际上好像可以用另外一种更为简单的数据结构完成,我不知道叫什么,当时也没怎么看懂……但是既然 已经打了线段树,我就直接又用了一棵线段树。
最后,细节方面,我们在输入区间的时候右端点要先加一,这个是为了区别与左端点,因为显然左右端点对区间的影响不同。但是在进行操作处理的时候相应的减一,具体代码如下(事实证明,把数据结构写在结构体里面有很大的好处):
#include<bits/stdc++.h>#define N 401000using namespace std;struct SegmentTree{int a[N];struct node{int l,r,min,lazy;} tree[4*N];inline void push_up(int i){tree[i].min=min(tree[i<<1].min,tree[i<<1|1].min);}inline void build(int i,int l,int r){tree[i].r=r;tree[i].l=l;tree[i].lazy=0;if (l==r) {tree[i].min=a[l];return;}int mid=(l+r)>>1;build(i<<1,l,mid);build(i<<1|1,mid+1,r);push_up(i);}inline void push_down(int i){if (!tree[i].lazy) return;int lazy=tree[i].lazy;tree[i<<1].min=lazy;tree[i<<1].lazy=lazy;tree[i<<1|1].min=lazy;tree[i<<1|1].lazy=lazy;tree[i].lazy=0; }inline void modify(int i,int l,int r,int x){if ((tree[i].l==l)&&(tree[i].r==r)){tree[i].min=x; tree[i].lazy=x; return;}push_down(i);int mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) modify(i<<1,l,r,x);else if (mid<l) modify(i<<1|1,l,r,x);else {modify(i<<1,l,mid,x); modify(i<<1|1,mid+1,r,x);}push_up(i);}inline int getmin(int i,int l,int r){if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].min;push_down(i);int mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) return getmin(i<<1,l,r);else if (mid<l) return getmin(i<<1|1,l,r);else return min(getmin(i<<1,l,mid),getmin(i<<1|1,mid+1,r));}} seg,seg1;//seg存储离散后的区间,seg1存储原区间struct query{int l,r,op,x;} q[N];int n,k,m,a[N];int getmin(int l,int r)//分块求区间最小{int L=(l-1)%n+1,R=(r-1)%n+1;//在块中的位置int Lblocks=(l-1)/n,Rblocks=(r-1)/n;//所在的块if (Lblocks==Rblocks) return seg1.getmin(1,L,R);else if (Lblocks+1==Rblocks) return min(seg1.getmin(1,L,n),seg1.getmin(1,1,R));else return seg1.getmin(1,1,n);}int main(){scanf("%d%d",&n,&k);for(int i=1;i<=n;i++){scanf("%d",&a[i]);seg1.a[i]=a[i];}seg1.build(1,1,n);scanf("%d",&m);vector<int> num;for(int i=1;i<=m;i++){scanf("%d%d%d",&q[i].op,&q[i].l,&q[i].r);if (q[i].op==1) scanf("%d",&q[i].x);num.push_back(q[i].l);num.push_back(++q[i].r);}sort(num.begin(),num.end());//离散重排vector<int>:: iterator end=unique(num.begin(),num.end());int cnt=end-num.begin();for(int i=0;i<cnt-1;i++)seg.a[i+1]=getmin(num[i],num[i+1]-1);//离散后区间取原区间的最小值seg.build(1,1,cnt-1);for(int i=1;i<=m;i++){q[i].l=lower_bound(num.begin(),end,q[i].l)-num.begin()+1;q[i].r=lower_bound(num.begin(),end,q[i].r)-num.begin();if (q[i].op==1) seg.modify(1,q[i].l,q[i].r,q[i].x); else printf("%d\n",seg.getmin(1,q[i].l,q[i].r)); }return 0;}
- CodeForces 802G Periodic RMQ Problem(线段树+分块思想)
- Educational Codeforces Round 20 G. Periodic RMQ Problem(线段树+主席树)
- Educational Codeforces Round 20 G. Periodic RMQ Problem(线段树动态开点)
- codeforces 803G Periodic RMQ Problem
- Codeforces803G Periodic RMQ Problem
- bzoj 3339: Rmq Problem(线段树)
- [BZOJ3339]Rmq Problem(离线+线段树)
- BZOJ3339: Rmq Problem 线段树
- poj_3468.A Simple Problem with Integers(线段树/分块)
- 【BZOJ3339】Rmq Problem【离线】【线段树】【mex】
- bzoj 3339 Rmq problem 离线+线段树
- bzoj3339 Rmq Problem(离线+线段树)
- [BZOJ3339]Rmq Problem 线段树应用
- CodeForces 52C Circular RMQ (区间更新线段树)
- codeforces #52 C Circular RMQ(线段树)
- Codeforces Round #413 C. Fountains(RMQ线段树)
- Codeforces 52C - Circular RMQ - 线段树
- 【CodeForces】52C Circular RMQ 线段树
- classloader 原理分析
- 免费视频播放器videojs中文教程
- 多重比较校正中的一些概念
- Android把View转换成Bitmap
- 【设计模式】观察者模式
- CodeForces 802G Periodic RMQ Problem(线段树+分块思想)
- 学习资料网址
- openvswitch处理upcall过程分析
- KUDU - Cloudera开发的又一个Hadoop系存储系统
- 仿京东、淘宝商品详情中上滑tableView的cell与headerView之间的动画效果
- Semaphore使用
- 线段树基本操作(1)
- Spring AOP讲解
- Android开发:Android提供的CountDownTimer结合TextView实现倒计时功能