| ||||||||||
| Online Judge | Problem Set | Authors | Online Contests | User | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Web Board Home Page F.A.Qs Statistical Charts | Current Contest Past Contests Scheduled Contests Award Contest | |||||||||
直接上主席树#include<cstdio>
#include<algorithm>
#include<vector>
#define N 100010
using namespace std;
/*
定义主席树的节点,
主席树是一坨权值线段树的剪切拼接,
每个节点的值是一个值域区间的数字出现的次数。
*/
struct node
{
int l,r;
int sum;
/*l,r分别表示左右儿子的编号,于是左右儿子就是hjt[l],hjt[r]
sum表示该节点的值*/
}hjt[N<<5];//对黄景泰dalao表示尊敬,, 大佬教我写代码。。。
long long org[N];
vector<long long>v;
inline int GetId(long long x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
int cnt=0;
int root[N];//记录根节点的编号
//依托上一个版本建立新的线段树。。。于是就变成主席树了
void insert(int l,int r,int pre,int &now,long long p)
{
hjt[++cnt]=hjt[pre];//先复制上一个版本的线段树,再修改
now=cnt; //要同步,,整了个引用
hjt[now].sum++;//新插入一个数
if(l==r) return;
int mid=(l+r)>>1;
//插入p,就是在线段树表示p的位置插入,,权值线段树。。权值线段树,,权值线段树
if(p<=mid) insert(l,mid,hjt[pre].l,hjt[now].l,p);
else insert(mid+1,r,hjt[pre].r,hjt[now].r,p);
}
/*
查询给定一个区间 x~y
前缀和思想,得到某一个区间的权值线段树
L代表x-1的版本的权值线段树遍历的当前节点
R表示y的版本的权值线段树遍历的当前节点
*/
long long query(int l,int r,int L,int R,int k)
{
if(l==r) return l;
int mid=(l+r)>>1;
/*
两个版本的线段树相减,
当前节点的左子树上的sum相减,得到新的线段树的左子树的sum值(前缀和思想)
得到的权值线段树的当前节点的sum就是左子树上数字的个数
*/
int temp=hjt[hjt[R].l].sum-hjt[hjt[L].l].sum;
if(k<=temp) return query(l,mid,hjt[L].l,hjt[R].l,k);
else return query(mid+1,r,hjt[L].r,hjt[R].r,k-temp);
}
int main()
{
int n,m;
int i;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%lld",&org[i]);
v.push_back(org[i]);//离散化处理
}
//排序,去重
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
//依次建立一坨版本的权值线段树
for(i=1;i<=n;i++)
{
//以前一个版本的根节点为依托
insert(1,n,root[i-1],root[i],GetId(org[i]));
}
int k;
for(k=1;k<=m;k++)
{
int r;
scanf("%d",&r);
printf("%lld\n",v[query(1,n,root[0],root[r],k)-1]);
}
return 0;
}
Followed by: Post your reply here: |
All Rights Reserved 2003-2013 Ying Fuchen,Xu Pengcheng,Xie Di
Any problem, Please Contact Administrator