我是靠谱客的博主 洁净钢笔,最近开发中收集的这篇文章主要介绍HDU 4006 离散化+权值线段树+线段树二分 UESTC 841 权值线段树,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

The kth great number

 
题目大意:
I操作表示在数列中添加一个数(序列最初为空)
Q操作表示求 数列中第k大的数

解题思路:
由于要求 数列中第k大的数,因此考虑使用 权值线段树。(详见  

UESTC 841 权值线段树

但是数据范围是10^6,考虑 离散化。
枚举每一次操作,将选出的数压入线段树。求后缀和为k的数的编号:
线段树二分,对于线段树的每一个结点,如果它右儿子的区间和大于等于k,则在右儿子中继续直到找到根节点。
                                                                  否则,在左儿子中找k=k- 右儿子的区间和,递归到根节点。
最后注意离散化的还原和读入输出优化
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
#define LL long long
const int N=1000010;
const int MAX=1e9+7;
int n,k;
struct Q
{
char c;
int a;
int b;
}q[N];
struct ha
{
int num;
int b;
int hao;
}a[N];
int len;
int d[N];
inline void R(int &v)//读入优化(必加,否则TLE)
{
v=0;
char ch=getchar();
int f=0;
while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
while(isdigit(ch)){v=(v<<3)+(v<<1)+ch-'0';ch=getchar();}
if(f) v=-v;
}
namespace ib {char b[100];}
inline void P(int x)//输出优化(必加,否则TLE)
{
if(x==0) {putchar(48); return;}
if(x<0) {putchar('-'); x=-x;}
char *s=ib::b;
while(x) *(++s)=x%10, x/=10;
while(s!=ib::b) putchar((*(s--))+48);
}
struct Segtree
{
struct trie
{
int l,r,len;
int sum;
int lz;
}tree[N<<2];
void updata(int o)//更新
{
tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum;
}
void build(int o,int l,int r)//建树
{
tree[o].sum=0;
tree[o].l=l;
tree[o].r=r;
tree[o].len=r-l+1;
if(l==r) {tree[o].sum=0;return;}
int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
updata(o);
}
void change(int o,int q,int v)//单点加
{
int l=tree[o].l,r=tree[o].r;
if(l==r) {tree[o].sum+=v;return;}
int mid=l+r>>1;
if(q<=mid) change(o<<1,q,v);
else change(o<<1|1,q,v);
updata(o);
}
int find(int o,int k)//线段树二分查找后缀和(必须为单调序列)
{
if(tree[o].l==tree[o].r) return tree[o].l;
int lo=o<<1,ro=o<<1|1;
if(tree[ro].sum>=k) return find(ro,k);
else return find(lo,k-tree[ro].sum);
}
}A;
void init()//初始化
{
len=0;
memset(d,0,sizeof(d));
memset(a,0,sizeof(a));
}
bool cmp(const ha &a,const ha &b)
{
return a.num<b.num;
}
int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
int i,j;
while(scanf("%d",&n)!=EOF)
{
init();
scanf("%d",&k);
for(i=1;i<=n;++i)
{
scanf("n");
q[i].c=getchar();
if(q[i].c=='I')R(q[i].a),a[++len].num=q[i].a,a[len].hao=i;
}
sort(a+1,a+len+1,cmp);//离散化
int x=0;
for(i=1;i<=len;++i)
{
if(a[i].num!=a[i-1].num) x++;
a[i].b=x;
d[x]=a[i].num;//指向原数
q[a[i].hao].b=a[i].b;//指向离散化后的数
}
A.build(1,0,x);
for(i=1;i<=n;++i)
{
if(q[i].c=='I') A.change(1,q[i].b,1);
else
{
P(d[A.find(1,k)]);//寻找后缀和为k的点
puts("");
}
}
}
return 0;
}
结语:
*离散化、*权值线段树、*线段树二分
较有难度的线段树题目。了解算法以后思路清晰才能写好。
线段树二分还会有很多应用,要熟练掌握。



最后

以上就是洁净钢笔为你收集整理的HDU 4006 离散化+权值线段树+线段树二分 UESTC 841 权值线段树的全部内容,希望文章能够帮你解决HDU 4006 离散化+权值线段树+线段树二分 UESTC 841 权值线段树所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部