树状数组好题
如果按照题意直接做,也就是在线算法,用任何一种数据结构都
难以实现,需要“树套树”即可持久化数据结构。
• 本题需要使用离线算法。也就是将所有询问先读进来再进行处理。
• 所谓“更改书”,可以理解为两种操作:先将原来的书删除,再
插入新的书
• 假设只有一种书(思维减法),那么只需要维护三种操作:
• 在第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;
}