POJ2104 K-th Number(主席树)
题目大意:查询区间k大值
关于主席树:
主席树(可持久化线段树,函数式线段树)
解决区间k大值 用到前缀和思想
如果前L-1的3个最大值1 2 3 前R个的3个最大值是4 5 6
那么前三大就是4 5 6
那如果前R个数前三大是1 5 6呢?那就必须要知道没个数的个数了!因为后面的数2与前面的数做差不就可以了吗?
但题目是k个数啊 那就维护k个数呗! 线段树!
建立n+1棵线段树,统计每个数到sz[i]时出现的次数,线段树R比线段树L-1多出来的数中第k大就是答案!但超空间啊
把所有数排序,去重,线段树中存储的不是数本身而是数的排序 还是空间......
解决方法:公用节点。
建立过程:
1.排序,去重
sz 2 4 6 8 8 6 4 2
hash 2 4 6 8
改成序号 1 2 3 4 4 3 2 1
2.建立线段树
先建立空线段树(要用到前缀和)
然后再根据每一个hash值建立线段树,每加一个数就增加一条链
实现跟线段树差不多
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//主席树模板 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=100005; const int maxnn=10000000; int root[maxn],ls[maxnn],rs[maxnn],cnt[maxnn],tot; int sz[maxn],hash[maxn]; void build(int &cur,int l,int r)//建立一棵空树 { cur=tot++; cnt[cur]=0; if(l!=r) { int mid=(l+r)/2; build(ls[cur],l,mid); build(rs[cur],mid+1,r); } } void updata(int pre,int ps,int &cur,int l,int r)//往上加线段树 { cur=tot++; cnt[cur]=cnt[pre]+1; ls[cur]=ls[pre];rs[cur]=rs[pre]; if(l==r)return; int mid=(l+r)/2; if(ps<=mid)updata(ls[pre],ps,ls[cur],l,mid); else updata(rs[pre],ps,rs[cur],mid+1,r); } int query(int lt,int rt,int l,int r,int k)//查询k大值 { if(l==r) return l; int mid=(l+r)/2,differ=cnt[ls[rt]]-cnt[ls[lt]]; if(k<=differ)return query(ls[lt],ls[rt],l,mid,k); else return query(rs[lt],rs[rt],mid+1,r,k-differ); } int main() { int n,m,l,r,k; while(scanf("%d%d",&n,&m)==2) { for(int i=1;i<=n;i++) { scanf("%d",sz+i); hash[i]=sz[i]; } sort(hash+1,hash+n+1); int size=unique(hash+1,hash+n+1)-hash-1;//数组去重 for(int i=1;i<=n;i++) sz[i]=lower_bound(hash+1,hash+size+1,sz[i])-hash;//得到序号 tot=0; build(root[0],1,size); for(int i=1;i<=n;++i) updata(root[i-1],sz[i],root[i],1,size); while(m--) { scanf("%d%d%d",&l,&r,&k); printf("%d\n",hash[query(root[l-1],root[r],1,size,k)]); } } return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//hja 大神的代码 指针版 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define nd(x) (nil + (x)) using namespace std; const int lim = 1e9; const int N = 100010; const int lg = 60; struct node { int s; node *lc, *rc; }; node nil[N * lg], *rot[N]; int cnt; int n, m, a[N], dc[N]; node* insert(node *u, int l, int r, int p) { node *ret = nd(++cnt); *ret = *u, ret->s ++; if(l == r) return ret; int mid = (l + r) / 2; if(p <= mid) ret->lc = insert(ret -> lc, l, mid, p); else ret->rc = insert(ret -> rc, mid + 1, r, p); return ret; } int query(node *u, node *v, int l, int r, int k) { if(l == r) return dc[l]; int mid = (l + r) / 2; int c = u -> lc -> s - v -> lc -> s; if(k <= c) return query(u -> lc, v -> lc, l, mid, k); return query(u -> rc, v -> rc, mid + 1, r, k - c); } void read() { cin >> n >> m; for(int i = 1; i <= n; i++) cin >> a[i]; } void prep() { for(int i=1; i<=n; i++) dc[i] = a[i]; sort(dc+1, dc+n+1), dc[0] = unique(dc+1, dc+n+1) - dc - 1; for(int i=1; i<=n; i++) a[i] = lower_bound(dc+1, dc+dc[0]+1, a[i]) - dc; } void solve() { cnt = nil->s = 0, rot[0] = nil->lc = nil->rc = nil; for(int i = 1; i <= n; i++) rot[i] = insert(rot[i-1], 1, dc[0], a[i]); while(m--) { int l, r, k; cin >> l >> r >> k; cout << query(rot[r], rot[l - 1], 1, dc[0], k) << endl; } } int main() { read(); prep(); solve(); return 0; }
折花枝,恨花枝,准拟花开人共卮,开时人去时。
怕相思,已相思,轮到相思没处辞,眉间露一丝。