1. 程式人生 > 其它 >【題解】[JSOI2009]計數問題

【題解】[JSOI2009]計數問題

Problem

\(\text{Solution:}\)

開始有一種暴力的做法:對每一行維護 \(100\) 個樹狀陣列對應 \(100\) 個顏色。查詢列舉行來查詢。

複雜度:\(O(m\cdot \log n\cdot q)\) 過不去的樣子。

考慮用二維樹狀陣列,直接維護二維矩陣。修改與查詢的複雜度都做到了 \(O(\log n\cdot \log m).\)

於是最終複雜度可以做到 \(O(q\log n\log m).\)

注意樹狀陣列實現上的細節:第二重迴圈要保證每次都是從 \(y\) 開始,所以要重新複製一個變數來做。而且兩個維度的最大值也是不一樣的,上界不同修改的時候要注意。

這題的樹狀陣列顯然沒有 上帝造題的七分鐘 那個樹狀陣列難。這題算是入門版的二維樹狀陣列。

維護矩陣同時維護一個顏色即可

#include<bits/stdc++.h>
using namespace std;
int tr[301][301][101],n,m,a[301][301],q;
inline int lowbit(int x){return x&(-x);}
void change(int x,int y,int c,int v){
	for(;x<=n;x+=lowbit(x))
		for(int i=y;i<=m;i+=lowbit(i))
			tr[x][i][c]+=v;
}
int query(int x,int y,int c){
	int res=0;
	for(;x;x-=lowbit(x))
		for(int i=y;i;i-=lowbit(i))
			res+=tr[x][i][c];
	return res;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j){
			scanf("%d",&a[i][j]);
			change(i,j,a[i][j],1);
		}
	scanf("%d",&q);
	for(;q;q--){
		int opt;
		scanf("%d",&opt);
		if(opt==1){
			int x,y,c;
			scanf("%d%d%d",&x,&y,&c);
			change(x,y,a[x][y],-1);
			a[x][y]=c;
			change(x,y,a[x][y],1);
		}
		else{
			int ax,bx,ay,by,c;
			scanf("%d%d%d%d%d",&ax,&bx,&ay,&by,&c);
			int ans=query(bx,by,c)-query(ax-1,by,c)-query(bx,ay-1,c)+query(ax-1,ay-1,c);
			printf("%d\n",ans);
		}
	}
	return 0;
}