1. 程式人生 > >P2464 [SDOI2008]鬱悶的小J [樹狀陣列+離線]

P2464 [SDOI2008]鬱悶的小J [樹狀陣列+離線]

傳送門

樹狀陣列好題

如果按照題意直接做,也就是線上演算法,用任何一種資料結構都
難以實現,需要“樹套樹”即可持久化資料結構。
• 本題需要使用離線演算法。也就是將所有詢問先讀進來再進行處理。
• 所謂“更改書”,可以理解為兩種操作:先將原來的書刪除,再
插入新的書

• 假設只有一種書(思維減法),那麼只需要維護三種操作:

• 在第i個位置插入書/在第i個位置刪除書。

• 詢問第i..j的位置有幾本書

• 用樹狀陣列可以輕鬆實

• 如何處理多種書的情況?

• 發現一個規律:無論是插入還是刪除第2種書,對於第1種的詢問都沒有任何影響。

• 因此只要將所有操作按照書的種類排序,分別處理即可

• 總結起來就是:

• 首先將修改操作拆成刪除舊書、插入新書兩種。加上詢問,共有三種操作。每種操作還需要記錄時間,也就是它是第幾個操作。

• 將三種操作按照第一關鍵字書的種類、第二關鍵字時間進行排

• 使用樹狀陣列維護插入、刪除、詢問三種操作。

• 由於按照種類排序,因此可以保證在任何時刻,樹狀陣列中的書都是同一類的(一類一類的完成)。

• 完成所有訊問後,將詢問操作排回原來的順序,輸出答案


#include<bits/stdc++.h>
#define N 100050*4
using namespace std;
struct Node{
	int time,id,op,kind,l,r; // 1-delete , 2 - add 3 - quary
}Q[N]; int n,m,tot,a[N],c[N],ans[N],cnt;
bool cmp(Node a,Node b){
	if(a.kind==b.kind) return a.time<b.time;
	return a.kind<b.kind;
}
void Up(int x,int val){for(;x<=n;x+=x&-x) c[x] += val;}
int Qu(int x){int ans=0; for(;x;x-=x&-x) ans += c[x]; return ans; }
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		Q[++tot] = Node{0,0,2,a[i],i,0};
	} 
	for(int i=1;i<=m;i++){
		char s[3]; scanf("%s",s);
		if(s[0]=='C'){
			int x,val; scanf("%d%d",&x,&val);
			if(a[x] != val){
				Q[++tot] = Node{i,0,1,a[x],x,0};
				Q[++tot] = Node{i,0,2,val,x,0};
				a[x] = val;
			}
		}
		if(s[0]=='Q'){
			int x,y,k; scanf("%d%d%d",&x,&y,&k);
			Q[++tot] = Node{i,++cnt,3,k,x,y};
		}
	}
	for(int i=1;i<=m;i++){
		Q[++tot] = Node{m+1,0,1,a[i],i,0};
	}
	sort(Q+1,Q+tot+1,cmp);
	for(int i=1;i<=tot;i++){
		if(Q[i].op==1) Up(Q[i].l,-1);
		if(Q[i].op==2) Up(Q[i].l,1); 
		if(Q[i].op==3) ans[Q[i].id] = Qu(Q[i].r) - Qu(Q[i].l-1);
	}
	for(int i=1;i<=cnt;i++) printf("%d\n",ans[i]);
	return 0;
}