LOJ #135. 二維樹狀數組 3:區間修改,區間查詢 題解

博客園同步

原題鏈接

前置知識:

一維樹狀數組的區間修改與區間查詢。

簡要題意:

維護二維數組的矩陣加與矩陣查。

很顯然,如果你用 二維線段樹 的話,常數較大,加上要開 long long\text{long long},很可能會 MLE + TLE\text{MLE + TLE} 的雙倍快樂。

所以我們要用 二維樹狀數組 解決這道題目。

考慮常規前綴和,二維的前綴和需要維護 44 個節點,我們也需要一一維護。

也就是我們要維護 44 個差分數組用來一一對應矩陣的四個角,並用 滾一維 的方式,把二維的更新。注意區間加和的細節。

時間複雜度:O(n2log2n)O(log2n)\mathcal{O}(n^2 \log^2 n) - \mathcal{O}(\log^2 n).

實際得分:100pts100pts.

雙倍經驗:P4514 上帝造題的七分鐘,不過這道題目需要大力卡常(本人卡了半天還是 8181,自閉了,LOJ\text{LOJ} 評測器最快!)

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(5000)
#pragma GCC optimize(1000000000)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include<bits/stdc++.h>
using namespace std;

const int N=(1<<12)+1;
#define int long long

inline int read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}

inline void write(int x) {
	if(x<0) {putchar('-');write(-x);return;}
	if(x<10) {putchar(char(x%10+'0'));return;}
	write(x/10);putchar(char(x%10+'0'));
}

int n,m;
struct BIT {
	int a[N][N];
	inline void add(int x,int y,int t) {
		for(register int i=x;i<=n;i+=(i&-i))
		for(register int j=y;j<=m;j+=(j&-j)) a[i][j]+=t;
	} //加

	inline int query(int x,int y) {
		int t=0;
		for(register int i=x;i>=1;i-=(i&-i))
		for(register int j=y;j>=1;j-=(j&-j)) t+=a[i][j];
		return t;
	} //詢問
}A,B,C,D; //四個樹狀數組

inline int calc(int x,int y) {
	return A.query(x,y)*(x*y+x+y+1)-B.query(x,y)*(y+1)
		-C.query(x,y)*(x+1)+D.query(x,y);
} //計算二維前綴和的答案

inline void Add(int x,int y,int t) {
	A.add(x,y,t); B.add(x,y,t*x);
	C.add(x,y,t*y); D.add(x,y,t*x*y);
} //修改四個節點

signed main() {
	n=read(),m=read(); int op;
	while(cin>>op) {
		int x1=read(),y1=read(),x2,y2,t;
		if(op==1) {
			x2=read(),y2=read(); t=read(); Add(x1,y1,t); Add(x1,y2+1,-t);
			Add(x2+1,y1,-t); Add(x2+1,y2+1,t);
		} else x2=x1,y2=y1,write(calc(x2,y2)-calc(x1-1,y2)-calc(x2,y1-1)+calc(x1-1,y1-1)),
		putchar('\n');
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章