https://acm.hdu.edu.cn/showproblem.php?pid=5249
易错点
- 管道头部是后进先出的
由于样例没有体现这一点,我想当然的以为是先进先出,然后……浪费了几个小时
不过这几个小时我加深了对权值线段树的理解,也算是塞翁失马吧
- 值域从0开始
这个一定要注意
解题思路
in x": 代表重要值为x(0≤x≤109) 的请求被推进管道。 |
query就是求整个区间第K大问题,权值线段树就可以搞定。然后需要注意的是值域1e9,不可能搞全树,肯定得动态开点,关于动态开点权值线段树可以看我的线段树总结https://mp.csdn.net/mp_blog/creation/editor/119553883
in和out操作意味着我们要获取以下几方面的信息
- 数列长度
- 队首元素
看到上面两点我们首先想到的就是模拟队列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;
}