zoukankan      html  css  js  c++  java
  • 分块

    分  块

    模板题

    传送门

    没有专门模板,就直接用线段树1啦,其实都一样

    数据结构讲解

    说句良心话,分块真的不难理解,甚至比树状数组,线段树更容易,只是效率偏低

    一句话概括分块:大段维护,局部朴素

    怎么理解呢?

    以模板题(区间求和,区间更新)为例,将原数列划分成t个块,每个块的大小不超过根号n,我们预处理出每一个块内的和,另外开一个add[](类似于线段树懒标记,但没有下传操作),存储每个块内增加的值(其实这里讲的不是很清楚,等下就明白了)

    预处理

    变量说明:

    int n , m , t;
    //n,m(见题目),t:段(块)的数量
    ll a[nn] , sum[nn] , add[nn];
    //a:见题目,sum:每一段(块)内a的和,add:等下讲
    int L[nn] , R[nn];
    //L:每一段(块)的左边界,R:每一段的右边界
    int pos[nn];
    //pos[i]:第i个点属于哪一个块
    
    void Init() {
    	t = sqrt(n);//此时的t既是段的数量,又是段的长度
    	for(int i = 1 ; i <= t ; i++)
    		L[i] = (i - 1) * t + 1,//这里可以自行推导一下
    		R[i] = i * t;
    	if(R[t] < n) {//处理剩下的点
    		++t;
    		L[t] = R[t - 1] + 1 , R[t] = n;
    	}
    	
    	for(int i = 1 ; i <= t ; i++)
    		for(int j = L[i] ; j <= R[i] ; j++)
    			pos[j] = i , sum[i] += a[j];
    }
    

    区间修改

    对于1操作(区间修改):[l,r]上的每一个数加上dat(变量与题目不同)

    1. 若l,r同属一个分块,直接暴力,将a[l~r]加上dat

    2. 否则,设l处于第p个分块,r属于第q个分块,

      1. (对于iin[p+1,q+1],add_i+=dat)

      2. 对于开头,结尾不足一整段的两部分,直接用朴素方法更新即可

    看代码:

    void change(int l , int r , ll dat) {
    	int p = pos[l] , q = pos[r];
    	if(p == q) {//l,r同属一个分块
    		for(int i = l ; i <= r ; i++)//暴力
    			a[i] += dat;
    		sum[p] += dat * (r - l + 1);//勿忘更新sum
    		return;
    	}
    	for(int i = l ; i <= R[p] ; i++)//对于开头不足一段的部分,暴力
    		a[i] += dat;
    	sum[p] += (R[p] - l + 1) * dat;
    	
    	for(int i = p + 1 ; i <= q - 1 ; i++)//中间,加到add即可,注意不需要更新sum,更具体的原因,查询部分会知道
    		add[i] += dat;
    	
    	for(int i = L[q] ; i <= r ; i++)//结尾不足一段,暴力
    		a[i] += dat;
    	sum[q] += (r - L[q] + 1) * dat;
    }
    

    到此,你应该理解add数组的用处了

    区间查询

    基本原理同区间修改,也是头尾不足一段的直接暴力,中间整段查询,这里不再赘述

    ll query(int l , int r) {
    	int p = pos[l] , q = pos[r];
    	ll ans = 0;
    	if(p == q) {
    		for(int i = l ; i <= r ; i++)
    			ans += a[i];
    		ans += add[p] * (r - l + 1);
    		return ans;
    	} 
    	for(int i = l ; i <= R[p] ; i++)
    		ans += a[i];
    	ans += add[p] * (R[p] - l + 1);
    	
    	for(int i = p + 1 ; i <= q - 1 ; i++)
    		ans += sum[i] + add[i] * (R[i] - L[i] + 1);
    	
    	for(int i = L[q] ; i <= r ; i++)
    		ans += a[i];
    	ans += add[q] * (r - L[q] + 1);
    	return ans;
    }
    

    模板题代码

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #define nn 100010
    #define ll long long
    using namespace std;
    ll read() {
    	ll re = 0;
    	bool sig = false;
    	char c;
    	do
    		if((c = getchar()) == '-')sig = true;
    	while(c < '0' || c > '9');
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
    	return sig ? -re : re;
    }
    int n , m , t;
    
    ll a[nn] , sum[nn] , add[nn];
    int L[nn] , R[nn];
    int pos[nn];
    
    void Init() {
    	t = sqrt(n);
    	for(int i = 1 ; i <= t ; i++)
    		L[i] = (i - 1) * t + 1,
    		R[i] = i * t;
    	if(R[t] < n) {
    		++t;
    		L[t] = R[t - 1] + 1 , R[t] = n;
    	}
    	
    	for(int i = 1 ; i <= t ; i++)
    		for(int j = L[i] ; j <= R[i] ; j++)
    			pos[j] = i , sum[i] += a[j];
    }
    void change(int l , int r , ll dat) {
    	int p = pos[l] , q = pos[r];
    	if(p == q) {
    		for(int i = l ; i <= r ; i++)
    			a[i] += dat;
    		sum[p] += dat * (r - l + 1);
    		return;
    	}
    	for(int i = l ; i <= R[p] ; i++)
    		a[i] += dat;
    	sum[p] += (R[p] - l + 1) * dat;
    	
    	for(int i = p + 1 ; i <= q - 1 ; i++)
    		add[i] += dat;
    	
    	for(int i = L[q] ; i <= r ; i++)
    		a[i] += dat;
    	sum[q] += (r - L[q] + 1) * dat;
    }
    ll query(int l , int r) {
    	int p = pos[l] , q = pos[r];
    	ll ans = 0;
    	if(p == q) {
    		for(int i = l ; i <= r ; i++)
    			ans += a[i];
    		ans += add[p] * (r - l + 1);
    		return ans;
    	} 
    	for(int i = l ; i <= R[p] ; i++)
    		ans += a[i];
    	ans += add[p] * (R[p] - l + 1);
    	
    	for(int i = p + 1 ; i <= q - 1 ; i++)
    		ans += sum[i] + add[i] * (R[i] - L[i] + 1);
    	
    	for(int i = L[q] ; i <= r ; i++)
    		ans += a[i];
    	ans += add[q] * (r - L[q] + 1);
    	return ans;
    }
    int main() {
    	n = read();	m = read();
    	for(int i = 1 ; i <= n ; i++)
    		a[i] = read();
    	Init();
    	for(int i = 1 ; i <= m ; i++) {
    		int l , r , dat;
    		if(read() == 1) {
    			l = read(),	r = read(), dat = read();
    			change(l , r , dat);
    		}
    		else {
    			l = read(),	r = read();
    			printf("%lld
    " , query(l , r));
    		}
    	}
    	return 0;
    } 
    
  • 相关阅读:
    phpcms相关
    php文件缓存
    js 、jq强化复习
    框架替换主页
    羽恒梦工厂所有后台的操作页面
    羽恒梦工厂个人中心
    羽恒梦工厂详情页
    待查的问题
    ASP.NET中的常用快捷键
    快速排序
  • 原文地址:https://www.cnblogs.com/dream1024/p/14033110.html
Copyright © 2011-2022 走看看