码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces 551E GukiZ and GukiZiana(分块思想)

时间:2017-07-24 01:23:37      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:不能   复杂   ++i   block   ace   改变   puts   操作   ret   

题目链接 GukiZ and GukiZiana

题目大意:一个数列,支持两个操作。一种是对区间$[l, r]$中的数全部加上$k$,另一种是查询数列中值为$x$的下标的最大值减最小值。

$n <= 500000, q <= 50000$

我一开始的反应是线段树,然后发现自己完全想错了……

这道题时限10秒,但也很容易超时。我后来是用分块过的。

把序列分成$\sqrt{n}$个块,每个块的大小为$\sqrt{n}$(最后一个块可能因为不能整除的关系可能会小一些)

每个块维护一个值$delta[i]$,表示这块的每一个数值都要加上这个值。

第1种操作的时候,找到$l$和$r$所在的块。

这两个块之间(不包含$l$所在的块和$r$所在的块,如果没有就不修改)的所有块的$delta$都加上$x$

这样就降低了修改的时间复杂度

$l$所在的块中的元素依次遍历,若下标满足$l <= i <= r$,则值加$x$

$r$所在的块中的元素依次遍历,若下标满足$l <= i <= r$,则值加$x$

每个块内按照值升序排序(第二关键字为下标)

当一个块的整体大小顺序可能发生改变时,就对这个块内部sort一遍,当然没必要sort的时候不要sort

不然可能TLE

查询的时候对每个块二分查找,找到值为$x$的元素的下标,并实时更新答案。

时间复杂度$O(q\sqrt{n}log(\sqrt{n}))$

 

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define fi		first
#define se		second

typedef long long LL;

const int N = 500010;
const int Q = 810;

int block_size, block_num, n, q, c[N], cnt, et, op, l, r;
LL a[N], delta[N], x;
vector <pair<LL, int> > block[Q];


void update(int l, int r, LL x){


	rep(i, c[l] + 1, c[r] - 1)
		delta[i] += x;

	for (auto &node : block[c[l]])
		if (node.se >= l && node.se <= r)
			node.fi += x;

	sort(block[c[l]].begin(), block[c[l]].end());

	if (c[r] > c[l]){
		for (auto &node : block[c[r]])
			if (node.se >= l && node.se <= r)
				node.fi += x;

		sort(block[c[r]].begin(), block[c[r]].end());

	}

}

void query(LL x){
	int L = 1 << 30, R = -1;
	rep(i, 1, block_num){
		auto it = lower_bound(block[i].begin(), block[i].end(), make_pair(x - delta[i], 0));
		if (it != block[i].end() && it -> first == x - delta[i])
			L = min(L, it -> se);

		it = lower_bound(block[i].begin(), block[i].end(), make_pair(x - delta[i] + 1, 0));
		if (it != block[i].begin()){
			--it;
			if (it -> fi == x - delta[i])
				R = max(R, it -> se);
		}
	}

	if (~R) printf("%d\n", R - L);
	else puts("-1");
}

int main(){


	scanf("%d%d", &n, &q);
	rep(i, 1, n) scanf("%lld", a + i);
	block_size = sqrt(n + 0.5);

	block_num = n / block_size;
	if (n % block_size) ++block_num;

	cnt = 1;
	rep(i, 1, n){
		++et;
		c[i] = cnt;
		block[cnt].push_back({a[i], i});
		if (et == block_size){
			et = 0;
			++cnt;
		}


	}

	rep(i, 1, block_num) sort(block[i].begin(), block[i].end());

	for (; q--; ){
		scanf("%d", &op);
		if (op == 1){
			scanf("%d%d%lld", &l, &r, &x); 
			update(l, r, x);
		}

		else{
			scanf("%lld", &x);
			query(x);
		}
	}

	return 0;
}

 

Codeforces 551E GukiZ and GukiZiana(分块思想)

标签:不能   复杂   ++i   block   ace   改变   puts   操作   ret   

原文地址:http://www.cnblogs.com/cxhscst2/p/7226758.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!