zoukankan      html  css  js  c++  java
  • 【ybt金牌导航6-3-2】区间计数(分块)(二分)

    区间计数

    题目链接:ybt金牌导航6-3-2

    题目大意

    给你一个数组,要你支持几个操作:
    区间加值和查询一个区间有多少个大于等于某个值的数。

    思路

    我们考虑用分块来做。
    区间加值很容易,暴力加和整块打标记就行。
    那接着问题是查询,维护区间有多少大于等于某个值的数看起来不现实。
    那我们考虑搞点东西,让它可以比较好的求。不难想到可以将区间内数排序,然后询问就二分。
    那你区间修改的时候整体修改不用重新排序,单独暴力修改的才要排序。
    那修改和询问都只是加了个 (logsqrt{n}),复杂度仍然可以接受。
    那就可以了。

    代码

    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    
    using namespace std;
    
    int n, q, a[1000002], sa[1000002];
    int S, s, bl[1002], br[1002];
    int lz[1002], x, y, z;
    char c;
    
    int read() {
    	int re = 0, zf = 1;
    	c = getchar();
    	while (c < '0' || c > '9') {
    		if (c == '-') zf = -zf;
    		c = getchar();
    	}
    	while (c >= '0' && c <= '9') {
    		re = (re << 3) + (re << 1) + c - '0';
    		c = getchar();
    	}
    	return re * zf;
    }
    
    void blocksort(int now) {//给这个块排序
    	for (int i = bl[now]; i <= br[now]; i++)
    		sa[i] = a[i];
    	sort(sa + bl[now], sa + br[now] + 1); 
    }
    
    void premake() {//一开始搞好每个块
    	for (int i = 1; i <= n; i++)
    		if (i % S == 1) {
    			br[s] = i - 1;
    			bl[++s] = i;
    		}
    	br[s] = n;
    	bl[s + 1] = br[s + 1] = n + 1;
    	for (int i = 1; i <= s; i++)
    		blocksort(i);
    }
    
    void add_val(int l, int r, int val) {
    	int L = (l - 1) / S + 1, R = (r - 1) / S + 1;
    	if (r - l + 1 <= 2 * S) {//直接暴力处理
    		for (int i = l; i <= r; i++) a[i] += val;
    		for (int i = L; i <= R; i++) blocksort(i);
    		return ;
    	}
    	bool loneyl = 1, loneyr = 1;
    	if (l == bl[L]) loneyl = 0, L--;//可能没有需要暴力处理的的
    	if (r == br[R]) loneyr = 0, R++;
    	for (int i = L + 1; i < R; i++)//整块的
    		lz[i] += val;
    	for (int i = l; i <= br[L]; i++)//两边暴力
    		a[i] += val;
    	if (loneyl) blocksort(L);
    	for (int i = bl[R]; i <= r; i++)
    		a[i] += val;
    	if (loneyr) blocksort(R);
    }
    
    int find(int b, int lim) {//二分找第一个大于等于的位置(然后得到大于等于的个数)
    	int l = bl[b], r = br[b], ans = br[b] + 1;
    	while (l <= r) {
    		int mid = (l + r) >> 1;
    		if (sa[mid] >= lim) {
    			ans = mid;
    			r = mid - 1;
    		}
    		else l = mid + 1;
    	}
    	return br[b] - ans + 1;
    }
    
    int query(int l, int r, int lim) {
    	int L = (l - 1) / S + 1, R = (r - 1) / S + 1, re = 0;
    	if (r - l + 1 <= 2 * S) {//暴力处理
    		for (int i = l; i <= r; i++)
    			if (a[i] + lz[(i - 1) / S + 1] >= lim)
    				re++;
    		return re;
    	}
    	if (l == bl[L]) L--; if (r == br[R]) R++;
    	for (int i = L + 1; i < R; i++) re += find(i, lim - lz[i]);//整块
    	for (int i = l; i <= br[L]; i++)//两边
    		if (a[i] + lz[L] >= lim) re++;
    	for (int i = bl[R]; i <= r; i++)
    		if (a[i] + lz[R] >= lim) re++;
    	return re; 
    }
    
    int main() {
    	n = read(); q = read();
    	for (int i = 1; i <= n; i++) a[i] = read();
    	
    	S = sqrt(n);
    	premake();
    	
    	while (q--) {
    		c = getchar();
    		while (c != 'M' && c != 'A') c = getchar();
    		if (c == 'M') {
    			x = read(); y = read(); z = read();
    			add_val(x, y, z);
    			continue;
    		}
    		if (c == 'A') {
    			x = read(); y = read(); z = read();
    			printf("%d
    ", query(x, y, z));
    			continue;
    		}
    	}
    	
    	return 0;
    }
    
    
  • 相关阅读:
    点击有惊喜
    模态框案例
    DOM操作
    定时器
    函数和object
    shell 判断文件出现次数
    shell 判断路径
    shell 循环数组
    shell 判断为空打印
    shell 示例1 从1叠加到100
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_6-3-2.html
Copyright © 2011-2022 走看看