1. 程式人生 > >[BZOJ3585][清華集訓2014]mex 主席樹

[BZOJ3585][清華集訓2014]mex 主席樹

考慮用主席樹維護到第i個位置時,每個數最後一次出現位置,也就是說維護的是n棵權值線段樹,線段樹上每個節點表示值域在此之內的每個數最後一次出現位置的最小值。
那麼一個詢問(l,r)就是在第r棵線段樹上查最小的且上一次出現位置小於l的數,經典操作。
注意離散化的時候要把每個值和它+1扔進去離散化,而且要去重,因為不能破壞整數的連續性。
程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const
int maxn=200010; int a[maxn],z[maxn<<1],n,m; struct tree; tree *NUL; struct tree { int mi; tree *so[2]; tree(){mi=0;so[0]=so[1]=NUL;} void update(){mi=min(so[0]->mi,so[1]->mi);} void build(tree *lst,int p,int l,int r) { if(l==r) {mi=p;return;} int mid=(l+r)>>1
;bool b=(a[p]>mid); int lx=b?mid+1:l,rx=b?r:mid; so[b^1]=lst->so[b^1]; (so[b]=new tree)->build(lst->so[b],p,lx,rx); update(); } int query(int p,int l,int r) { if(l==r||this==NUL) return l; int mid=(l+r)>>1; if(so[0]->mi<p) so[0
]->query(p,l,mid); else so[1]->query(p,mid+1,r); } }*rt[maxn]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); z[2*i-1]=a[i];z[2*i]=a[i]+1; } sort(z+1,z+2*n+1); int sz; sz=unique(z+1,z+2*n+1)-z-1; for(int i=1;i<=n;i++) a[i]=lower_bound(z+1,z+sz+1,a[i])-z; NUL=new tree;NUL->so[0]=NUL->so[1]=NUL; rt[0]=new tree; for(int i=1;i<=n;i++) (rt[i]=new tree)->build(rt[i-1],i,1,sz); while(m--) { int l,r; scanf("%d%d",&l,&r); printf("%d\n",z[rt[r]->query(l,1,sz)]); } return 0; }