zoukankan      html  css  js  c++  java
  • luo3372线段树模板的分块做法

    题目大意

    请你维护一个有n个元素的整数序列,要求支持区间查询&区间修改

    对于100%的数据,(1<=n<=10^5)

    分析

    正常做法是线段树维护区间修改、区间查询,今天我要讲的是一种暴力做法:分块

    分块的思想并不复杂,分块把一个长度为n的区间分成num段,操作时如果是整段用标记修改,不是整段的部分暴力修改

    分析时间复杂度:在这题中,每段的标记修改是(O(1))的,最多有num段,整段标记修改所用时间是(O(num))的;不是整段的部分最多有(O(n/num))个,暴力修改所用的时间是(O(n/num))的;所以总时间是(O(num+n/num))

    根据基本不等式,num取(sqrt n)时该式有最小值;所以num取(sqrt n)

    实现

    分块的思想并不复杂,时间复杂度也不玄学,但是实现起来并不方便(可能是我弱)

    分块的修改/查询都分为2部分:

    1. 整块的修改
    2. “零头”的修改

    整块的修改是否简便:add[i] += val;
    “零头”的修改直接修改,同时不要忘了维护所在块的信息:

    a[i] += val;
    sum[id[i]] += val;
    

    修改就愉快地解决了,查询和修改差不多。

    完整代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    const int maxn = 100007;
    int n, m, num, id[maxn];
    long long sum[1000], add[1000], a[maxn];
    
    int main(){
    	scanf("%d%d", &n, &m);
    	num = sqrt(n);
    	for (int i = 1; i <= n; ++i){
    		scanf("%lld", &a[i]);
    		id[i] = (i-1) / num;
    		sum[id[i]] += a[i];
    	}
    
    	while (m--){
    		int d; scanf("%d", &d);
    		if (d==1){
    			int x, y, C; scanf("%d%d%d", &x, &y, &C);
    			int Leftid = (x-1) / num + 1;
    			int Rightid = (y-1) / num - 1;
    
    			long long res = 0;
    			for (int i = Leftid; i <= Rightid; ++i)
    				 add[i] += C;
    
    			for (int i = x; i <= Leftid * num; ++i)
    				a[i] += C, sum[id[i]] += C;
    
    			for (int i = (Rightid+1)*num+1; i <= y; ++i)
    				a[i] += C, sum[id[i]] += C;
    		}else{
    			int x, y; scanf("%d%d", &x, &y);
    			int Leftid = (x-1) / num + 1;
    			int Rightid = (y-1) / num - 1;
    
    			if (id[x] == id[y]){
    				long long res = 0;
    				for (int i = x; i <= y; ++i)
    					res += a[i] + add[id[i]];
    				printf("%lld
    ", res);
    				continue;
    			}
    
    			long long res = 0;
    			for (int i = Leftid; i <= Rightid; ++i)
    				res += sum[i] + add[i] * num;
    
    			for (int i = x; i <= Leftid * num; ++i)
    				res += a[i] + add[Leftid-1];
    
    			for (int i = (Rightid+1)*num+1; i <= y; ++i)
    				res += a[i] + add[Rightid+1];
    
    			printf("%lld
    ", res);
    
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    hdu6325 /// 上凸包
    hdu6315 /// 线段树区间更新
    hdu6311 /// 欧拉路径 无向图最小路径覆盖 输出正反路径
    Codeforces Round #535 F-MST Unification
    HDU4405 Aeroplane chess (概率DP,转移)
    ZOJ 3329 One Person Game(概率DP,求期望)
    poj3744 (概率DP+矩阵快速幂)
    “美登杯”上海市高校大学生程序设计 E. 小花梨的数组 (线段树)
    关于标记的一些事~~
    关于C(n,m) 的奇偶 ,与C(n,0),C(n,1),C(n,2)…C(n,n).当中有多少个奇数
  • 原文地址:https://www.cnblogs.com/YJZoier/p/9721774.html
Copyright © 2011-2022 走看看