Problem Description:
你工作以后, KPI 就是你的全部了. 我开发了一个服务,取得了很大的知名度。数十亿的请求被推到一个大管道后同时服务从管头拉取请求。让我们来定义每个请求都有一个重要值。我的KPI是由当前管道内请求的重要值的中间值来计算。现在给你服务记录,有时我想知道当前管道内请求的重要值得中间值。
Input:
有大约100组数据。
每组数据第一行有一个n(1≤n≤10000),代表服务记录数。
接下来有n行,每一行有3种形式
“in x”: 代表重要值为x(0≤x≤109)的请求被推进管道。
“out”: 代表服务拉取了管道头部的请求。
"query: 代表我想知道当前管道内请求重要值的中间值. 那就是说,如果当前管道内有m条请求, 我想知道,升序排序后第
f
l
o
o
r
(
m
/
2
)
+
1
t
h
floor(m/2)+1th
floor(m/2)+1th条请求的重要值.
为了让题目简单,所有的x都不同,并且如果管道内没有值,就不会有"out"和"query"操作。
output:
对于每组数据,先输出一行
Case # i:
然后每一次"query",输出当前管道内重要值的中间值。
Input:
6
in 874
query
out
in 24622
in 12194
query
output:
Case #1:
874
24622
思路:
离线操作,离散化数据,找到原数组的数据在排序后的数组的下标,将下标放进树中,之后再进行寻找第k大数(k=中间值)
详细
- 离线操作
- 离散化
- 第k大数
- 首先我们进行的应该是离线操作,因为题目给出的数据都是一次一次的给出,所以当我们要建树的时候就碰到了困难,所以我们先需要离线操作,例如题目中的,给出的六组操作,我们可以令 1 代表 in,2 代表 out,3 代表 query,每次将操作取出以后存到一个队列里,例如:
queue<int >q;
for(int i=1;i<=6;i++)
{
char demand[10];
scanf("%s%*c",demand);
if(demand[0]=='i')
q.push(1);
else
if(demand[0]=='o')
q.push(2);
else
q.push(3);
}
- 将我们的操作处理好之后,我们就可以得知被推进管道里有多少个数据,之后我们就可以进行建树,并且离散化这些数据。
- 开辟两个数组,f[N]存放原数据,b[N]存放排序后的数据,(其实也可以用队列来存放原数据),之后,定义两个指针,都是用在f[N]上,一个是指向将要删除的数据,一个是指向将要放在树里的数据。
//根据操作之后原数据
f[3]={874,24622,12194}
//排序之后的数据
b[3]={274,12194,24622}
//根据f[i]在b中所对应的下标,进行操作
//874----1 24622---3 12194---2
//根据值找下标 -> lower_bound()
按照操作,依次放到树里,即在树中就已经排好了顺序,我们就可以在树中找到中间值。
AC代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <queue>
#define N 20100
#define ll long long
using namespace std;
ll n,f[N],k,b[N];
ll sum;
deque<ll> cmd,dq;
struct T
{
ll l;
ll r;
ll tag;
ll sum;
} tree[N*4];
void Pushup(ll nt)
{
tree[nt].sum=tree[nt*2].sum+tree[nt*2+1].sum;
}
void Build_tree(ll l,ll r,ll nt)
{
tree[nt].l = l;
tree[nt].r = r;
tree[nt].tag=0;
if(l==r)
{
tree[nt].sum=0;
return ;
}
else
{
ll mid = (l+r)/2;
Build_tree(l,mid,2*nt);
Build_tree(mid+1,r,2*nt+1);
}
Pushup(nt);
}
void Update(ll nt)
{
tree[2*nt].sum+=(tree[2*nt].r-tree[2*nt].l+1)*tree[nt].tag;
tree[2*nt+1].sum+=(tree[2*nt+1].r-tree[2*nt+1].l+1)*tree[nt].tag;
tree[2*nt].tag+=tree[nt].tag;
tree[2*nt+1].tag+=tree[nt].tag;
tree[nt].tag=0;
}
void Query_tree(ll a,ll nt)
{
if(tree[nt].l==tree[nt].r)
{
sum=b[tree[nt].l];
return ;
}
if(tree[2*nt].sum>=a)
Query_tree(a,2*nt);
else
Query_tree(a-tree[2*nt].sum,2*nt+1);
}
void Insert_tree(ll a,ll nt)
{
if(tree[nt].l==tree[nt].r)
{
tree[nt].sum=1;
return ;
}
ll mid=(tree[nt].l+tree[nt].r)/2;
if(mid>=a)
Insert_tree(a,2*nt);
else
Insert_tree(a,2*nt+1);
Pushup(nt);
}
void Delete_tree(ll a,ll nt)
{
if(tree[nt].l==tree[nt].r)
{
tree[nt].sum=0;
return ;
}
ll mid=(tree[nt].l+tree[nt].r)/2;
if(mid>=a)
Delete_tree(a,2*nt);
else
Delete_tree(a,2*nt+1);
Pushup(nt);
}
int main()
{
ll t,x=1;
while(~scanf("%lld%*c",&t)) //离线操作
{
ll point1=0,point=0,cnt=0;
memset(f,0,sizeof(f));
memset(b,0,sizeof(f));
memset(tree,0,sizeof(tree));
printf("Case #%lld:\n",x++);
for(int i=1;i<=t;i++)
{
char demand[10];
scanf("%s%*c",demand);
if(demand[0]=='i')
{
cnt++;
scanf("%lld%*c",&f[cnt]);
b[cnt]=f[cnt];
cmd.push_back(1);
}
else
if(demand[0]=='o')
cmd.push_back(2);
else
if(demand[0]=='q')
cmd.push_back(3);
}
ll len=cnt;
Build_tree(1,cnt,1);
sort(b+1,b+len+1);
cnt=0;
for(int i=1;i<=t;i++)
{
ll oper=cmd.front();
cmd.pop_front();
if(oper==1)// Next : in
{
point1++; //在原数组中即将放到树中的下标
cnt++;
ll data=lower_bound(b+1,b+len+1,f[point1])-b;
Insert_tree(data,1);
}
else
if(oper==2) //删除操作
{
point++;//模拟队列首
cnt--;
ll delete_sub=lower_bound(b+1,b+len+1,f[point])-b;
Delete_tree(delete_sub,1);
}
else
if(oper==3) //查找操作
{
Query_tree(cnt/2+1,1);
printf("%lld\n",sum);
}
}
}
return 0;
}
Tips:
这里我都用的long long,其中一些变量用int就好,每次操作全部完成之后,不要忘记数组清零(WA了三遍 ),代码写的不好,勿喷 !