HDU 5249解题报告 权值线段树+动态开点+队列模拟

https://acm.hdu.edu.cn/showproblem.php?pid=5249

易错点

  • 管道头部是后进先出的

由于样例没有体现这一点,我想当然的以为是先进先出,然后……浪费了几个小时

不过这几个小时我加深了对权值线段树的理解,也算是塞翁失马吧

  • 值域从0开始

这个一定要注意

解题思路

in x": 代表重要值为x(0≤x≤109) 的请求被推进管道。
  "out": 代表服务拉取了管道头部的请求。
  "query: 代表我想知道当前管道内请求重要值的中间值. 那就是说,如果当前管道内有m条请求, 我想知道,升序排序后第floor(m/2)+1th 条请求的重要值.

query就是求整个区间第K大问题,权值线段树就可以搞定。然后需要注意的是值域1e9,不可能搞全树,肯定得动态开点,关于动态开点权值线段树可以看我的线段树总结https://mp.csdn.net/mp_blog/creation/editor/119553883

in和out操作意味着我们要获取以下几方面的信息

  1. 数列长度
  2. 队首元素

看到上面两点我们首先想到的就是模拟队列https://mp.csdn.net/mp_blog/creation/editor/119360001

int queue[N];
int head=0,tail=1;

queue数组当队列使用,head是队首,初始化为0,tail是队尾,初始化为1,这个数组我们从1开始使用(个人习惯,如果喜欢从0使用可以调整,比如tail=0),所以我们加入元素的时候是queue[++head]=t;删除元素的时候是tail++;数列长度很简单,head-tail+1就行了

那么以上注意点全都get到之后这题就是一道水题了(get不到会死得很惨)

#include<cstdio>//(动态开点权值线段树)
#include<iostream>
#include<cstring>
#include<string>
#define lnode tree[node].lson
#define rnode tree[node].rson
using namespace std;
typedef long long ll;
const int N=1e7+10;//动态树最大结点数
const int MAX=1e9;//题中给的值域
struct node {
	ll sum;//该点的权值(出现的次数)
	int lson,rson;//左子节点,右子节点
} tree[N];
int n,cnt=0,root;//cnt计数变量, 记录开了多少节点
int qu[N],head,tail,t;//模拟队列
inline int read() {
	int s=0,w=1;char c=getchar();
	while(c<'0' || c>'9') {	if(c=='-')	w*=-1;	c=getchar();}
	while(c>='0' && c<='9') {s=(s<<3)+(s<<1)+c-'0';	c=getchar();}
	return s*w;
}
inline void push_up(int node) {//更新父节点
	tree[node].sum=tree[lnode].sum+tree[rnode].sum;
}
void update(int &node,int start,int end,int k,int del) { //node是人为规定的编号,所以传引用 k的权值+1 start是当前节点区间下限 end是区间上限 不是左右子节点!
	if(!node) //新建节点
		node=++cnt;
	if(start==end) {
		tree[node].sum+=del;
		return;
	}
	register int mid=(start+end)>>1;
	if(k<=mid) update(lnode,start,mid,k,del);
	else update(rnode,mid+1,end,k,del);
	push_up(node);
}
int kth(int node,int start,int end,int k) { //查询第k大值是多少 第k小等价于n-k+1大 n为叶子结点总数
	if(start==end)
		return start;
	register int mid=(start+end)>>1,s1=lnode,s2=rnode;//s1是左子树地址,s2是右子树地址
	if(k<=tree[s1].sum)
		return kth(lnode,start,mid,k);//向右子树搜索
	else
		return kth(rnode,mid+1,end,k-tree[s1].sum);//向左子树搜索
}
inline void init() {
	for(int i=0; i<=cnt; i++) 
		tree[i].lson=tree[i].rson=tree[i].sum=0;
	cnt=root=head=0,tail=1;
}
int main() {
	while(scanf("%d",&n)!=EOF) {
		init(),++t;
		printf("Case #%d:\n",t);
		for(int i=1; i<=n; i++) {
			string a;
			cin>>a;
			if(a[0]=='i') {
				register int b=read();
				qu[++head]=b;
				update(root,0,MAX,b,1);
			} else if(a[0]=='o') {
				update(root,0,MAX,qu[tail++],-1);//数组长度head-tail+1
			} else if(a[0]=='q') {
				int m=((head-tail+1)>>1)+1;
				printf("%d\n",kth(root,0,MAX,m));
			}
		}
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值