zoukankan      html  css  js  c++  java
  • Codeforces 551E GukiZ and GukiZiana(分块思想)

    题目链接 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(qsqrt{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
    ", 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;
    }
    
  • 相关阅读:
    【瞎口胡】基础数学 1 快速幂 整除 最大公约数
    【瞎口胡】二分图
    Windos下使用Redis
    VUE的踩坑日记(1)
    公告
    [维度打击]最大连续子序列
    常用函数
    树状数组
    高精度封装
    T4 模板之 单个文件
  • 原文地址:https://www.cnblogs.com/cxhscst2/p/7226758.html
Copyright © 2011-2022 走看看