bzoj 2120 数颜色 树状数组套可持久化数据结构。

查询区间l,r 中 不同值的个数, 修改操作是 单点更新。

如果没有更新,此题直接 离线。

有更新的话,就只有 树状数组套 平衡树 或者 可持久化数据结构。

问题一 。统计 在 【1,l-1】中都多少个值没有出现在 【l,r】。也就是统计 【1,l-1】中有多少个值,在【1,r】中仅出现在 【1,l-1】。

统计 对于ai ,它的下次出现与ai 值相同的点的下标是多少。 相当于 把所有相同的值的下标放到同一棵set 中,查 i 的后继是多少。

所以按值构建2*n 棵set ,来维护2*n个值出现的下标。快速的找到后继。(值是离散化后的值,下标i,离散化后值 a, 那么st【a】中插入 i)。


序列n个点, 共n个线段树, 每个线段树中维护的是 是 又是后继的下标, 有点绕。

查询  【1,l-1】 查大于r的值有多少个,就是 问题一的解。


问题二 : 在区间 【1,r】中出现了多少个值, 在set 中查找如果是 begin() , 就是说最小的数, 那么+1 ,不是最小不加。


修改是个难点 :

问题一种 维护的是后继, 相当与 链表的修改,

删除点 a ,它的前驱的后继发生变化。

增添点a,增添后的前驱的后继也发生变化。

他这个点的前驱和后继发生变化。

这样 共需要 6次操作。

问题二中较简单。

这题AC的历程有点艰辛。 代码有点长

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <cstring>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <assert.h>
#include <queue>
#define REP(i,n) for(int i=0;i<n;i++)
#define TR(i,x) for(typeof(x.begin()) i=x.begin();i!=x.end();i++)
#define ALLL(x) x.begin(),x.end()
#define SORT(x) sort(ALLL(x))
#define CLEAR(x) memset(x,0,sizeof(x))
#define FILLL(x,c) memset(x,c,sizeof(x))
using namespace std;
const double eps = 1e-9;
#define LL long long 
#define pb push_back
const int maxn  = 11000;
int num[maxn];
int n , m;
map<int ,int>mp;
map<int,int>::iterator it;
char op[maxn][4];
int t[maxn][3];
struct Node{
    Node *l,*r;
    int sum;
};
int tot;
struct Seg{
	Node nodes[maxn*60];
	Node *null;
	Node *root[maxn];
	int C;
	void init(){
          C= 0;
          null = &nodes[C++];
          null->l = null ->r = null;
          null->sum = 0;
          for(int i=1;i<=n;i++){
          	          root[i] = &nodes[C++];  
                      root[i]->l = null;  
                      root[i]->r = null;  
                      root[i]->sum  = 0; 
          }
	}
	Node *update(int pos,int left,int right,Node *root,int val){
       Node * rt = root;  
       if(rt == null){  
           rt = &nodes[C++];  
           rt->l = null;  
           rt->r = null;  
           rt->sum = 0;  
       }  
       if(left == right){  
            rt->sum +=val;  
            return rt;  
       }  
       int mid = (left + right)/2;  
       if(pos<=mid){  
          rt->l = update(pos,left,mid,rt->l,val);  
       }else{  
          rt->r = update(pos,mid+1,right,rt->r ,val);  
       }  
       rt->sum = rt->l->sum + rt->r->sum ;
       return rt;  
    }
    void update(int k,int pos,int val){
    	 for(;k<=n;k+= k&(-k)){
    	 	update(pos,1,n+1,root[k],val);
    	 }
    }
    int  query(int left ,int right,int L,int R,Node *root){
    	if(L>R)return 0;
    	  if(L<=left && right<=R){
    	  	   return root->sum;
    	  }
    	  int mid = (left +right)/2;
    	  int ret = 0;
    	  if(L<=mid){
    	  	  ret += query(left,mid,L,R,root->l);
    	  }
    	  if(R>=mid){
    	  	  ret += query(mid+1,right,L,R,root->r);
    	  }
    	  return ret;
    }
    // 大于等于 a 的值 有多少个
    int get(int k,int a){
    	 int sum = 0 ;
    	 for(;k>0;k-=k&(-k)){
    	 	 sum += query(1,n+1,a,n+1,root[k]);
    	 }
    	 return sum;
    }
}S1,S2;
set<int>st[maxn*2];
set<int>::iterator it1,it2,it3;

int arr[maxn*2];
void inc(int k,int val){
	if(k==0)return ;
	for(;k<=n;k+=k&(-k)){
		arr[k]+=val;
	}
}
int get(int k){
	int sum = 0;
	for(;k>0;k-=k&(-k)){
		sum += arr[k];
	}
	return sum;
}
void solve(){
	S1.init();
	//cout << "*********"<<endl;
	CLEAR(arr);
	for(int i=1;i<=tot;i++){
		st[i].clear();
		st[i].insert(n+1);
	}
	for(int i=1;i<=n;i++){
		 st[mp[num[i]]].insert(i);
	}
	// 初始化 arr
	for(int i=1;i<=n;i++){
	      int v = mp[num[i]];
	      it1 =st[v].lower_bound(i);
	      if(it1==st[v].begin()){
	      //	cout << i<< "***"<<endl;
	      	 inc(i,1);
	      }
    }
	for(int i=1;i<=n;i++){
		 int v = mp[num[i]];
	     it1 = st[v].upper_bound(i);
	     S1.update(i,(*it1),1);
	     //cout <<i  << "  "<< (int)(*it1)<<endl;
	}
	for(int i=1;i<=m;i++){
	//	cout << "starti"<<i<<endl;
		  if(op[i][0]=='Q'){
		  	  int l = t[i][0];
		  	  int r = t[i][1];
		  //	  cout <<r << " wtf" <<mp[r]<<endl;
		  	  int ans1 = S1.get(l-1,r+1);
		  	  int ans2 = get(r);
		  	 
		  	  int ans = ans2 -ans1;
		  	 // cout << ans2 << " "<< ans1<<endl;
		  	  printf("%d\n",ans);
		  }else{
		  	  int id = t[i][0];
		  	  int val = t[i][1];
		  	  if(num[id]==val)continue;
		  	  // 实现 删除。
		  	  int v = mp[num[id]];
		  	  it1 = st[v].lower_bound(id);
		  	  if(it1==st[v].begin()){
		  	  	 inc(id,-1);
		  	  	 it1++;
		  	  	 inc((*it1),1);
		  	  }
		  	  // SEGMENT 
		  	  it3 =it2 = it1 = st[v].lower_bound(id);
		  	  it2++;
		  	  S1.update(id,(*it2),-1);
		  	  if(it1 != st[v].begin()){
		  	  	   it1--;
		  	       S1.update((*it1),id,-1);
		  	       S1.update((*it1),(*it2),1);
		  	  }
		  	  st[v].erase(it3);
		  	  //geng新arr
		  	  
		  	  // 实现增添。
		  	  num[id] = val;
		  	  v = mp[num[id]];
		  	  st[v].insert(id);
		  	  //gengxin arr
		  	  it1 =st[v].lower_bound(id);
		  	  if(it1==st[v].begin()){
		  	  	  inc(id,1);
		  	  	  it1++;
		  	  	  inc((*it1),-1);
		  	  }//SEGMENT
		  	  it2 = it1 = st[v].lower_bound(id);
		  	  it2++;
		  	  if(it1 != st[v].begin()){
		  	        it1--;
		  	        S1.update((*it1),(*it2),-1);
		  	        S1.update((*it1),id,1);
		  	  }
		  	  S1.update(id,(*it2),1);
		  	  
		 }
	}
}
int idx[maxn*2];
int main(){
    while(~scanf("%d%d",&n,&m)){
    	mp.clear();
    	for(int i=1;i<=n;i++){
    		scanf("%d",&num[i]);
    		mp[num[i]]=1;
    	}
    	for(int i=1;i<=m;i++){
    		scanf("%s",op[i]);
    		if(op[i][0]== 'Q'){
    			 scanf("%d%d",&t[i][0],&t[i][1]);
    			 mp[t[i][1]]=1;
    		}else{
    			scanf("%d%d",&t[i][0],&t[i][1]);
    			mp[t[i][1]]=1;
    		}
    	}
    	tot = 0;
    	for(it = mp.begin(); it!= mp.end();it++){
    		 tot ++ ;
    		 it->second = tot;
    		 idx[tot] = it->first;
    	}
    	
    	tot++;
    	solve();
    }   
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值