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;
}

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/85013741