1. 程式人生 > 實用技巧 >Mokia CDQ分治+容斥

Mokia CDQ分治+容斥

Mokia CDQ分治+容斥

題目描述

維護一個\(W \times W\)的矩陣,初始值均為\(S\).每次操作可以增加某格子的權值,或詢問某子矩陣的總權值.修改運算元\(M \leq 160000\),詢問數\(Q \leq 10000,W \leq 2000000\).

輸入格式

第一行兩個整數\(S,W\);其中\(S\)為矩陣初始值;\(W\)為矩陣大小
接下來每行為一下三種輸入之一(不包含引號):
\("1\ x\ y\ a"\)
\("2\ x1\ y1\ x2\ y2"\)
\("3"\)
輸入\(1\):你需要把\((x,y)\)(第\(x\)

行第\(y\)列)的格子權值增加\(a\)
輸入\(2\):你需要求出以左下角為\((x1,y1)\),右上角為\((x2,y2)\)的矩陣內所有格子的權值和,並輸出
輸入\(3\):表示輸入結束

輸出格式

對於每個輸入\(2\),輸出一行,即輸入\(2\)的答案

樣例

樣例輸入

0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3

樣例輸出

3
5

資料範圍與提示

保證答案不會超過\(int\)範圍

分析

我們會發現格子很大,但是修改數和查詢數比較小

因此,我們肯定不能維護整個格子,而要從修改和查詢上找突破口

我們設第\(i\)次修改的點的橫縱座標分別為 \(nx,ny\)

,如果它要對之後的某個矩形產生貢獻的話

必須滿足 \(x_1 \leq nx \leq x_2,y_1\leq ny \leq y_2\)

如果加上時間的限制,那麼就是一個五維的偏序,不好處理

我們可以用容斥的方法把一個大矩形分成四個小矩形,類似於求字首和的方法

\(sum[x_2][y_2]-sum[x_2][y_1-1]-sum[x_1-1][y2]+sum[x_1-1][y_1-1]\)

\(sum[i][j]\) 表示原點和 \((i,j)\) 圍成的矩形所增加的值

這樣問題就變成了一個三維偏序 \(i<j,x[i] \leq x[j],y[i] \leq y[j]\)

然後就可以離線用 \(CDQ\)

分治解決

程式碼

#include<cstdio>
#include<algorithm>
inline int read(){
	int x=0,fh=1;
	char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=2e6+5;
struct asd{
	int id,x,y,wz,val;
	bool jud;
	asd(){}
	asd(int aa,int bb,int cc,int dd,int ee,bool ff){
		id=aa,x=bb,y=cc,wz=dd,val=ee,jud=ff;
	}
}b[maxn];
int s,w,cnt,js;
bool cmp1(asd aa,asd bb){
	if(aa.id==bb.id && aa.x==bb.x) return aa.y<bb.y;
	if(aa.id==bb.id) return aa.x<bb.x;
	return aa.id<bb.id;
}
bool cmp2(asd aa,asd bb){
	if(aa.x==bb.x) return aa.y<bb.y;
	return aa.x<bb.x;
}
int lb(int xx){
	return xx&-xx;
}
int tr[maxn];
void ad(int wz,int val){
	for(int i=wz;i<maxn;i+=lb(i)){
		tr[i]+=val;
	}
}
int cx(int wz){
	int nans=0;
	for(int i=wz;i>0;i-=lb(i)){
		nans+=tr[i];
	}
	return nans;
}
int ans[maxn][6];
void solve(int l,int r){
	if(l==r) return;
	int mids=(l+r)>>1;
	solve(l,mids);
	solve(mids+1,r);
	std::sort(b+l,b+mids+1,cmp2);
	std::sort(b+mids+1,b+r+1,cmp2);
	int now=l;
	for(int i=mids+1;i<=r;i++){
		while(now<=mids && b[now].x<=b[i].x){
			if(!b[now].jud){
				ad(b[now].y,b[now].val);
			}
			now++;
		}
		if(b[i].jud){
			ans[b[i].id][b[i].wz]+=cx(b[i].y);
		}
	}
	for(int i=now-1;i>=l;i--){
		if(!b[i].jud){
			ad(b[i].y,-b[i].val);
		}
	}
}
int jl[maxn],anss[maxn];
int main(){
	s=read(),w=read();
	while(1){
		int aa,bb,cc,dd,ee;
		aa=read();
		js++;
		jl[js]=aa;
		if(aa==1){
			bb=read(),cc=read(),dd=read();
			bb++,cc++;
			b[++cnt]=asd(js,bb,cc,0,dd,0);
		} else if(aa==2){
			bb=read(),cc=read(),dd=read(),ee=read();
			bb++,cc++,dd++,ee++;
			b[++cnt]=asd(js,dd,ee,1,0,1);
			b[++cnt]=asd(js,dd,cc-1,2,0,1);
			b[++cnt]=asd(js,bb-1,ee,3,0,1);
			b[++cnt]=asd(js,bb-1,cc-1,4,0,1);
			anss[js]=(dd-bb+1)*(ee-cc+1)*s;
		} else {
			break;
		}
	}
	std::sort(b+1,b+1+cnt,cmp1);
	solve(1,cnt);
	for(int i=1;i<js;i++){
		if(jl[i]==2){
			printf("%d\n",ans[i][1]-ans[i][2]-ans[i][3]+ans[i][4]+anss[i]);
		}
	}
	return 0;
}