區間第k小的問題咱們能夠用靜態主席樹來維護,可是一些題目每每會增長修改操做,那麼咱們應該怎麼作呢,先看例題。node
給定一個含有n個數的序列a[1],a[2],a[3]……a[n],程序必須回答這樣的詢問:對於給定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的數是多少(1≤k≤j-i+1),而且,你能夠改變一些a[i]的值,改變後,程序還能針對改變後的a繼續回答上面的問題。你須要編一個這樣的程序,從輸入文件中讀入序列a,而後讀入一系列的指令,包括詢問指令和修改指令。 輸入格式: 第一行有兩個正整數n(1≤n≤10000),m(1≤m≤10000)。分別表示序列的長度和指令的個數。 第二行有n個數,表示a[1],a[2]……a[n],這些數都小於10^9。接下來的m行描述每條指令,每行的格式是下面兩種格式中的一種。 Q i j k 或者 C i t Q i j k (i,j,k是數字,1≤i≤j≤n, 1≤k≤j-i+1)表示詢問指令,詢問a[i],a[i+1]……a[j]中第k小的數。 C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改變成爲t。 輸出格式: 對於每一次詢問,你都須要輸出他的答案,每個輸出佔單獨的一行。
這道題若是隻用靜態主席樹是不可能的,由於有修改操做,靜態主席樹是不能修改的,咱們就須要能支持修改的動態主席樹。 咱們想,單點修改,區間查詢,這不是樹狀數組最擅長的嗎。可是顯然,樹狀數組是不能維護這個東西的,因此咱們就須要套一個主席樹去維護,用一顆主席樹維護原序列的信息,再用主席樹維護一個樹狀數組(其實這個算是樹套樹了)去維護修改操做的信息。c++
首先仍是離散化,但要注意,修改的值也須要離散化,由於修改的值和原值位置不一樣,因此這裏咱們選擇用指針對地址進行操做數組
void work(){ sort(dis+1,dis+num+1,cmp);p[0]=-1;/*dis表示一個指針變量的數組,num表示須要離散的數據的數量*/ for(int i=1,j=0;i<=num;++i) { if(*dis[i]!=p[j])p[++j]=*dis[i]; *dis[i]=j; } }
###構建 維護原序列的主席樹跟靜態主席樹同樣,直接複製過來也能夠,但爲了防止打掛,仍是重打一遍更好。而維護樹狀數組的主席樹其實也差很少函數
###修改 用樹狀數組維護一個區間修改的信息,每個節點的範圍跟樹狀數組沒有區別,但咱們須要用主席樹去維護這個樹狀數組,樹狀數組的每個節點都是一顆值域線段樹,保存樹狀樹狀每個節點的根,每次修改就對樹狀數組包含這個元素的節點進行修改,每次修改都至關於刪除一個元素再插入一個元素,每次維護都要基於這個節點的本來信息進行修改(+1或-1)。因爲每次都要維護log個節點,每一個節點要新增$log$個節點因此時空複雜度均爲$\log^2$。 建樹和維護本質上都是基於一顆原線段樹進行修改,因此可使用同一個函數進行操做。spa
int modify(int l,int r,int x,int k,int o){ int y=++cnt; t[y]=t[x];t[y].x+=o; if(l==r)return y; int mid=(l+r)>>1; if(k<=mid)t[y].l=modify(l,mid,t[x].l,k,o); else t[y].r=modify(mid+1,r,t[x].r,k,o); return y; }
###查詢 查詢時,咱們既要查詢原序列的信息,又要查詢修改信息,因此咱們須要把樹狀數組須要查詢的節點所有儲存到一個數組裏,再進行查詢,原序列的查詢方式跟靜態主席樹同樣,樹狀數組的查詢就與樹狀數組的區間查詢同樣,只不過把每次訪問節點改成這個節點表明的值域線段樹,注意,每一次查詢的全部有關信息的訪問必須同時進行。指針
int query(int l,int r,int s1,int s2,int k){ if(l==r)return l; int x=t[t[s2].l].x-t[t[s1].l].x; for(int i=1;i<=tot1;++i)x-=t[t[q1[i]].l].x; for(int i=1;i<=tot2;++i)x+=t[t[q2[i]].l].x; int mid=(l+r)>>1; if(x>=k) { for(int i=1;i<=tot1;++i)q1[i]=t[q1[i]].l; for(int i=1;i<=tot2;++i)q2[i]=t[q2[i]].l; return query(l,mid,t[s1].l,t[s2].l,k); } else { for(int i=1;i<=tot1;++i)q1[i]=t[q1[i]].r; for(int i=1;i<=tot2;++i)q2[i]=t[q2[i]].r; return query(mid+1,r,t[s1].r,t[s2].r,k-x); } }
#include<bits/stdc++.h> using namespace std; inline int gi(){ char a=getchar();int b=0; while(a<'0'||a>'9')a=getchar(); while(a>='0'&&a<='9')b=b*10+a-'0',a=getchar(); return b; } const int N=1e4+50; struct node {int l,r,x;} t[N*900]; struct ppp {int l,r,op,k;} b[N]; int cmp(int* x,int* y) {return *x<*y;} int a[N],p[N*5],n,m,tot1,tot2,lshh,cnt=1,root[N],root1[N],q1[N],q2[N]; int *lsh[N*5]; void work(){ sort(lsh+1,lsh+lshh+1,cmp);p[0]=-1; for(int i=1,j=0;i<=lshh;++i) { if(*lsh[i]!=p[j])p[++j]=*lsh[i]; *lsh[i]=j; } } int modify(int l,int r,int x,int k,int o){ int y=++cnt; t[y]=t[x];t[y].x+=o; if(l==r)return y; int mid=(l+r)>>1; if(k<=mid)t[y].l=modify(l,mid,t[x].l,k,o); else t[y].r=modify(mid+1,r,t[x].r,k,o); return y; } int query(int l,int r,int s1,int s2,int k){ if(l==r)return l; int x=t[t[s2].l].x-t[t[s1].l].x; for(int i=1;i<=tot1;++i)x-=t[t[q1[i]].l].x; for(int i=1;i<=tot2;++i)x+=t[t[q2[i]].l].x; int mid=(l+r)>>1; if(x>=k) { for(int i=1;i<=tot1;++i)q1[i]=t[q1[i]].l; for(int i=1;i<=tot2;++i)q2[i]=t[q2[i]].l; return query(l,mid,t[s1].l,t[s2].l,k); } else { for(int i=1;i<=tot1;++i)q1[i]=t[q1[i]].r; for(int i=1;i<=tot2;++i)q2[i]=t[q2[i]].r; return query(mid+1,r,t[s1].r,t[s2].r,k-x); } } int main(){ cin>>n>>m; for(int i=1;i<=n;++i) { a[i]=gi(); lsh[++lshh]=&a[i]; } for(int i=1;i<=m;++i) { char aa=getchar(); while(!(aa=='Q'||aa=='C'))aa=getchar(); b[i].l=gi(); b[i].r=gi(); if(aa=='C') { b[i].op=1; lsh[++lshh]=&b[i].r; } else b[i].k=gi(); } work(); for(int i=1;i<=n;++i) root1[i]=root[1]; for(int i=1;i<=n;++i) root[i]=modify(1,lshh,root[i-1],a[i],1); for(int i=1;i<=m;++i) if(b[i].op) { int x=b[i].l,y=b[i].r,s=a[x];a[x]=y; while(x<=n) { root1[x]=modify(1,lshh,root1[x],s,-1); root1[x]=modify(1,lshh,root1[x],y,1); x+=(x&(-x)); } } else { tot1=0,tot2=0;int x=b[i].l-1; while(x){q1[++tot1]=root1[x];x-=(x&(-x));}x=b[i].r; while(x){q2[++tot2]=root1[x];x-=(x&(-x));} printf("%d\n",p[query(1,lshh,root[b[i].l-1],root[b[i].r],b[i].k)]); } return 0; }