您的位置:首页 > 其它

POJ 2104 第K大的数, 主席树,

2015-12-29 13:58 387 查看
第一次接触这种神奇的数据结构,感觉不错。有学了个好东西,也不难。他主要应该是针对于数据统计的,例如本题的第k大的数。算法的主要思想是 先对给定的数离散化,然后在线段树中保存数字出现的次数(即叶子节点会存该节点所对应的数字出现的次数,非叶结点则保存子节点的数字之和)(这就与我们普通的线段树不同了)。然后我们会对于 从 a1 到 ai ( 1<=i<=n )之间的数据建一棵线段树,因此总共会建n+1棵,(看到这,你会不会担心MLE呢?其实不会的,因为节点是可以共用的)。 建完后,如果 查询是针对 ai 到 aj 这个区间的,那么我们就可以用aj那棵线段树上的值减去 ai-1 那棵线段树上的值,那么就可以得到在这个区间中各个数字出现的次数。加油!

真奇怪,为什么 POJ 上写read(),读入优化反而慢了700多毫秒。求大神赐教。

#include<cstdio>
#include<iostream>
#include<algorithm>
#define rep(i,j,k) for(int i = j; i <= k; i++)
using namespace std;
int root[100005], a[100005], p[100005];
int n_nodes = 0, n_numb;

struct node{
int times, lc, rc;
} nod[3000000];

void build(int l,int r,int&p){
p = ++n_nodes;
nod[p].lc = nod[p].rc = nod[p].times = 0;
if( l >= r ) return;
int mid = (l+r)/2;
build(l,mid,nod[p].lc); build(mid+1,r,nod[p].rc);
}

void build2(int l,int r,int&p, int pre,int mre)
{
p = ++n_nodes;
nod[p] = nod[pre];
nod[p].times++;
if( l >= r ) return;
int mid = (l+r)/2;
if( mre <= mid ){
build2(l,mid,nod[p].lc,nod[pre].lc,mre);
}
else {
build2(mid+1,r,nod[p].rc,nod[pre].rc,mre);
}
}

int ask(int l,int r,int pre,int p,int k)
{
if( l >= r ) return l;
int s = nod[nod[p].lc].times - nod[nod[pre].lc].times;
int mid = (l+r) / 2;
if( s >= k ){
return ask(l,mid,nod[pre].lc,nod[p].lc,k);
}
else {
return ask(mid+1,r,nod[pre].rc,nod[p].rc,k-s);
}
}

int main()
{
int n, q;
scanf("%d %d",&n,&q);
rep(i,1,n){
scanf("%d", &a[i]);
p[i] = a[i];
}
sort(p+1,p+1+n);
n_numb = unique(p+1,p+1+n) - p - 1;
build(1,n_numb,root[0]);
rep(i,1,n){
int m = lower_bound(p+1,p+n_numb+1,a[i]) - p;
build2(1,n_numb,root[i],root[i-1],m);
}
rep(i,1,q){
int x, y, k;
scanf("%d %d %d",&x,&y,&k);
int m = ask(1,n_numb,root[x-1],root[y],k);
printf("%d\n", p[m]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: