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

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 queries a 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 as1 l r x — set all elements in the segment froml till r (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 1
1 2 3
3
2 1 3
1 1 2 4
2 1 3
Output
1
3
Input
3 2
1 2 3
5
2 4 4
1 4 4 5
2 4 4
1 1 6 1
2 6 6
Output
1
5
1


        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
#define N 401000
using 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>1;
		if (mid>=r) return getmin(i<<1,l,r);
		else if (mid 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:: iterator end=unique(num.begin(),num.end());
	int cnt=end-num.begin();
	for(int i=0;i


你可能感兴趣的:(线段树,---------Online,Judge--------,CodeForces,分块算法)