1. 程式人生 > 實用技巧 >P2357 守墓人(分塊)

P2357 守墓人(分塊)

傳送門

演算法分析

  • 區間修改 區間查詢 單點修改 單點查詢 顯然可以用線段樹做
  • 但是我們用另外一種神奇的暴力把這個 2e5的題碾過就會很舒服
  • 分塊是一種比較常見的思想 分塊也就是分治 把一個大區間分成幾個小區間計算
  • 我們可以把每\(log_n\) 個數劃分成一組 如果區間修改了整個塊的話 不用去挨個修改單點的值 而是直接改掉整個塊的資訊 然後用lazy記錄一下修改操作
  • 如果並沒有修改掉整個塊 那麼我們就可以暴力處理 因為如果沒有覆蓋整個塊 則操作元素個數肯定小於\(2*log_n\) 那麼暴力處理也不會有太大的時間損耗
  • 如果查詢的時候 查詢區間跨過了整個塊 那麼我們直接統計塊的資訊就可以了 如果並沒有跨過整個塊 那麼我們也一樣直接暴力處理 注意先把每個塊的lazy先處理掉 複雜度一樣不會太高
  • 然後預處理一下每個點所屬於的塊就好了 注意修改和查詢時的lazy的修改以及一些細節 比如修改的區間左端點和右端點同屬於一個塊

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
typedef long long ll;
int a[maxn];
ll sum[maxn];
int lazy[maxn];
int bel[maxn];
int size;

void change(int l,int r,int k){//區間修改
	if(bel[l] == bel[r]){ //暴力處理 l 和 r 在同一個塊的情況
		for(int i = l;i <= r;++i) a[i] += k,sum[bel[l]] += k;
		return;
	}
	for(int i = l;i <= bel[l] * size;++i) a[i] += k,sum[bel[l]] += k; // 暴力處理左邊
	for(int i = r;i >= (bel[r] - 1) * size + 1;--i) a[i] += k,sum[bel[r]] += k; // 暴力處理右邊
	for(int i = bel[l] + 1;i <= bel[r] - 1;++i)	lazy[i] += k,sum[i] += size * k; // 處理中間的塊(這些塊一定是整個覆蓋掉的)
}

void ask(int l,int r){
	ll ans = 0;
	if(lazy[bel[l]]) {//把左端點的塊的lazy下放
		for(int i = (bel[l] - 1) * size + 1;i <= bel[l] * size;++i) a[i] += lazy[bel[l]];
		lazy[bel[l]] = 0;
	}
	if(lazy[bel[r]]) {//右端點的塊的lazy下放
		for(int i = bel[r] * size;i >= (bel[r] - 1) * size + 1;--i) a[i] += lazy[bel[r]];
		lazy[bel[r]] = 0;
	}
        //中間塊的lazy沒必要處理 因為我們只需要統計區間和 而lazy對塊的sum是沒有影響的
	if(bel[l] == bel[r]) {//暴力處理左右端點屬於同一個塊的情況
		for(int i = l;i <= r;++i) ans += a[i];
		printf("%lld\n",ans);
		return;
	}
	for(int i = l;i <= bel[l] * size;++i) ans += a[i];//暴力處理左端點所屬的塊
	for(int i = r;i >= (bel[r] - 1) * size + 1;--i) ans += a[i];//暴力處理右端點所屬於的塊
	for(int i = bel[l] + 1;i <= bel[r] - 1;++i) ans += sum[i]; // 處理中間塊
	printf("%lld\n",ans);
}

int main(){
	int n,m;scanf("%d%d",&n,&m);
	size = sqrt(n);
	for(int i = 1;i <= n;++i){
		bel[i] = (i - 1) / size + 1;
		scanf("%d",&a[i]);
		sum[bel[i]] += a[i];
	}
	for(int i = 1;i <= m;++i){
		int flag;scanf("%d",&flag);
		if(flag == 1) {int l,r,k; scanf("%d%d%d",&l,&r,&k); change(l,r,k);}
		if(flag == 2) {int k;scanf("%d",&k); a[1] += k; sum[1] += k;}
		if(flag == 3) {int k;scanf("%d",&k); a[1] -= k; sum[1] -= k;}
		if(flag == 4) {int l,r; scanf("%d%d",&l,&r); ask(l,r);}
		if(flag == 5) printf("%d\n",a[1]);
	}
	return 0;
}