欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1901
题意概括
给你一段序列(n个数),让你支持一些操作(共m次),
有两种操作,一种是询问区间第k小,一种是单点修改。
n,m<=10000
题解
这个主席树的写法是我自己造出来的。
主席树的查询区间第k大需要依赖前缀和。
树状数组最擅长这个了,就让他来干。
原理是这样的:
先离散化,包括修改操作里面的数字也要离散化。
然后建树,包括修改操作所涉及的数值也要建。
现在总共有n个线段树。
第0棵树是最完整的,故我们把这棵树的每一个节点都当作一个树状数组的起点,即该树状数组的第一项。
然后往下一棵树走,当然下一棵树不一定有这个节点,那么不停的走下去,这个可以在建树的时候预先处理一个Next数组来优化。
走到下一个这个位置的节点之后,我们再把这个节点作为该树状数组的第二项。
这样我们有大约n*4(第0棵树的节点个数)个树状数组了。
然而不处理要超空间。
所以有两种方案,一种是vector,一种是把所有的树状数组连起来,变成一个大的。
然而查询之类的就和普通的主席树差不多了。
细节上要大大的注意!
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> #include <vector> using namespace std; const int N=10005,S=N*2*20*4;//v=4*node=4*20*hs=4*20*2*n struct Que{ char op; int a,b,c; void read(){ char opc[5]; scanf("%s%d%d",opc,&a,&b); op=opc[0]; if (op=='Q') scanf("%d",&c); } }q[N]; vector <int> vec[N]; int n,m,v[N],Ha[N*2],hs=0,total=0,totarr=0; int root[N],ls[S],rs[S],sum[S],pos[S],Next[S],p[S]; void LSH(){ int hs_=1; sort(Ha+1,Ha+hs+1); for (int i=2;i<=hs;i++) if (Ha[i]!=Ha[i-1]) Ha[++hs_]=Ha[i]; hs=hs_; } int find(int x){ int L=1,R=hs,mid; while (L<=R){ mid=(L+R)>>1; if (Ha[mid]==x) return mid; if (Ha[mid]<x) L=mid+1; else R=mid-1; } exit(233); } int build(int L,int R){ int rt=++total; if (L==R){ ls[rt]=rs[rt]=0; return rt; } int mid=(L+R)>>1; ls[rt]=build(L,mid); rs[rt]=build(mid+1,R); return rt; } int lowbit(int x){ return x&-x; } void add(int x,int v){ for (;x<=totarr;x+=lowbit(x)) sum[x]+=v; } int Sum(int x){ int ans=0; for (;x>0;x-=lowbit(x)) ans+=sum[x]; return ans; } void add_tree(int prt,int &rt,int L,int R,int pos){ if (!rt||rt==prt) rt=++total; Next[prt]=rt; if (L==R) return; int mid=(L+R)>>1; if (pos<=mid){ add_tree(ls[prt],ls[rt],L,mid,pos); if (!rs[rt]) rs[rt]=rs[prt]; } else { add_tree(rs[prt],rs[rt],mid+1,R,pos); if (!ls[rt]) ls[rt]=ls[prt]; } } void build_treearr(int srt,int L,int R){ for (int i=srt;i;i=Next[i]) p[i]=++totarr; if (L==R) return; int mid=(L+R)>>1; build_treearr(ls[srt],L,mid); build_treearr(rs[srt],mid+1,R); } void update(int rt,int L,int R,int pos,int v){ add(p[rt],v); if (L==R) return; int mid=(L+R)>>1; if (pos<=mid) update(ls[rt],L,mid,pos,v); else update(rs[rt],mid+1,R,pos,v); } int query(int prt,int rt,int L,int R,int k){ if (L==R) return Ha[L]; int mid=(L+R)>>1; int LV=Sum(p[ls[rt]])-Sum(p[ls[prt]]); if (k<=LV) return query(ls[prt],ls[rt],L,mid,k); else return query(rs[prt],rs[rt],mid+1,R,k-LV); } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&v[i]),Ha[++hs]=v[i]; for (int i=1;i<=m;i++){ q[i].read(); if (q[i].op=='C') Ha[++hs]=q[i].b; } LSH(); for (int i=1;i<=n;i++) vec[i].clear(); for (int i=1;i<=n;i++) vec[i].push_back(v[i]); for (int i=1;i<=m;i++) if (q[i].op=='C') vec[q[i].a].push_back(q[i].b); memset(sum,0,sizeof sum); memset(pos,0,sizeof pos); memset(Next,0,sizeof Next); memset(root,0,sizeof root); root[0]=build(1,hs); for (int i=1;i<=n;i++) for (int j=0;j<vec[i].size();j++) add_tree(root[i-1],root[i],1,hs,find(vec[i][j])); build_treearr(root[0],1,hs); for (int i=1;i<=n;i++) update(root[i],1,hs,find(v[i]),1); for (int i=1;i<=m;i++) if (q[i].op=='C'){ update(root[q[i].a],1,hs,find(v[q[i].a]),-1); update(root[q[i].a],1,hs,find(v[q[i].a]=q[i].b),1); } else printf("%d ",query(root[q[i].a-1],root[q[i].b],1,hs,q[i].c)); return 0; }