1. 程式人生 > >不帶修改主席樹模板

不帶修改主席樹模板

對於一部分線段樹看似無法直接做的題,可以用主席樹來做。
主席樹就是對每個字首開一棵線段樹,當然,直接這樣會MLE。
可以使用一種類似動態開節點的方法可以有效避免MLE。
具體可以參考我的部落格,那裡寫的更詳細一點:可持久化線段樹
因為主席樹是由字首加起來的,所以區間[l,r]的解可以類似字首和那樣a[r]-a[l-1]直接減
主席樹的詳細解答可以看看這裡

建樹
主程式

fo(i,1,n) {
        root[i]=++tot;t[tot]=t[root[i-1]];
        build(root[i],1,n,c[i]);
    }

build

void
build(int v,int i,int j,int z) { t[v].d++;if(i==j) return; int m=(i+j)/2; if(z<=m) t[++tot]=t[t[v].l],t[v].l=tot,build(t[v].l,i,m,z);

查詢請直接看下面的完整程式碼

模板題:不帶修改的區間K大(小)數查詢 poj2104
題目連結

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 101000 using namespace std; int n,a[N],b[N],c[N],root[N],tot=0; struct note{ int l,r,d; }; note t[N*50]; bool cnt(int i,int j){return a[i]<a[j];} void build(int v,int i,int j,int z) { t[v].d++;if(i==j) return; int m=(i+j)/2; if(z<=m) t[++tot]=t[t[v].l],t[v].l=tot,build(t[v].l,i,m,z); else
t[++tot]=t[t[v].r],t[v].r=tot,build(t[v].r,m+1,j,z); } int find(int v1,int v2,int i,int j,int k) { if(i==j) return i; int jy=t[t[v2].l].d-t[t[v1].l].d,m=(i+j)/2; if (jy>=k) return find(t[v1].l,t[v2].l,i,m,k); else return find(t[v1].r,t[v2].r,m+1,j,k-jy); } int main() { int m;scanf("%d%d",&n,&m); fo(i,1,n) scanf("%d",&a[i]),b[i]=i; sort(b+1,b+n+1,cnt); fo(i,1,n) c[b[i]]=i; fo(i,1,n) { root[i]=++tot;t[tot]=t[root[i-1]]; build(root[i],1,n,c[i]); } for(;m;m--) { int x,y,k;scanf("%d%d%d",&x,&y,&k); printf("%d\n",a[b[find(root[x-1],root[y],1,n,k)]]); } }