CodeForces 802G Periodic RMQ Problem(线段树+分块思想)

来源:互联网 发布:印度最新人口数据 编辑:程序博客网 时间:2024/06/06 08:45

G. Periodic RMQ Problem
time limit per test:4 seconds
memory limit per test:512 megabytes
input:standard input
output:standard output

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

Input

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

Output

For each query of type 2 print the answer to this query — the minimum on the corresponding segment.

Examples
Input
3 11 2 332 1 31 1 2 42 1 3
Output
13
Input
3 21 2 352 4 41 4 4 52 4 41 1 6 12 6 6
Output
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;}


0 0
原创粉丝点击