基准时间限制:1.5 秒 空间限制:131072 KB 分值: 80
难度:5级算法题
数据流统计功能上线后,为51nod提升用户体验做出了很大的贡献。但是新问题随之而来,夹克老爷还想知道在一个窗口内,访问次数最多用户(即窗口内的众数)。如果有多个众数,取用户ID最小的一个。(窗口的意思是一个固定长度的区间!)
(因为数据流是实时的、在线的,所以不允许使用离线算法^_^)
Input
第一行为整数n, k。(1 <= n <= 5 * 10^6,1 <= k <= 1000) n代表有多少次操作,k代表窗口大小。 接下来的n行,每行代表一次操作。每行第一个整数为操作数。 操作数1:用户访问 输入格式: <1, id> 用户ID为[0, INT_MAX]闭区间内的整数。代表拥有此ID的用户对网站进行了一次访问,窗口进行相应移动。 操作数2:询问众数 输入格式:<2> 输出窗口内的众数,如果有多个,输出ID最小的那个。 p.s. 对于询问众数的操作,窗口保证不空 p.s.s. 对于询问众数的操作,窗口可能不满
Output
对于询问众数的操作,每行输出一个整数。
Input示例
10 5 1 2 1 1 1 2 1 1 1 2 1 1 2 1 3 1 3 2
Output示例
1 1
题目大意:
起初看到这道题,没读懂是什么意思,一直在纳闷窗口是啥。后来才明白,是实现查询,查询时的前k个访问次数中,访问最多的用户的编号。
分析:
首先,考虑到的是只考虑查询前的前k次访问,复杂度是k*查询次数,如果大部分都是查询明显过不去。
之后,用优先队列去实现,优先队列内容是一个结构体,储存了用户的ID,和访问次数。但是,优先队列不支持删除操作,
所以怎么去实现删除已经超过k个用户的数据呢?我的方法是使用map记录每个用户的访问次数,删除k个之前的用户时,只在map里将其更新。然后,查询时
去循环取优先队列顶部元素,检查这个元素储存的访问次数是否与map里记录的次数相同,如果不相同则该节点作废,出队,再取队列顶部元素,直到相同为止,
然后,交上去,就超时了,将map改成hash_map,优化了输入之后再交,700ms通过。
其实,优先队列写法挂了之后,并没又马上用hash_map,就将程序改为了用set去实现,set的最大值就是set中最后一个元素,插入删除复杂度,log(n),2000ms,超时。。。。。。然后又将map改成hash_map,优化了输入输出之后再交,
700ms通过。。。。。。
ps:hash_map用VC++去交就可以成功编译
//优先队列实现
#include<iostream>
using namespace std;
#include<queue>
#include<cstdio>
#include<hash_map>
typedef long long LL;
inline LL getint()//快速读入
{
LL w = 0, q = 0;
char c = getchar();
while ((c<'0' || c>'9') && c != '-') c = getchar();
if (c == '-') q = 1, c = getchar();
while (c >= '0'&&c <= '9') w = w * 10 + c - '0', c = getchar();
return q ? -w : w;
}
queue<LL> team;
struct data//优先队列里信息用的结构体
{
LL sj;
LL bh;
bool operator<(const data &b)const
{
if (this->sj == b.sj) return this->bh>b.bh;
return this->sj<b.sj;
}
};
priority_queue<data> preteam;
hash_map<LL, LL> maps;
int n, k;
int main()
{
int rq = 0;
cin >> n >> k;
LL pre;
int ml;
LL dx;
data mx;
for (int i = 1; i <= n; i++)
{
//scanf("%d", &ml);
ml = getint();
if (ml == 1)
{
//scanf("%lld", &dx);
dx = getint();
data np;
maps[dx]++;
np.bh = dx; np.sj = maps[dx];
preteam.push(np);
team.push(dx);
if (team.size() == k + 1)
{
pre = team.front();
maps[pre]--;
team.pop();
}
}
if (ml == 2)
{
mx = preteam.top();
/*循环取优先队列有效值*/
while (mx.sj != maps[mx.bh] && !preteam.empty()) {
preteam.pop();
mx = preteam.top();
}
printf("%d\n", mx.bh);
}
}
return 0;
}
//set实现
#include<iostream>
using namespace std;
#include<queue>
#include<cstdio>
#include<hash_map>
#include<map>
#include<set>
typedef long long LL;
queue<LL> team;
inline LL getint()
{
LL w = 0, q = 0;
char c = getchar();
while ((c<'0' || c>'9') && c != '-') c = getchar();
if (c == '-') q = 1, c = getchar();
while (c >= '0'&&c <= '9') w = w * 10 + c - '0', c = getchar();
return q ? -w : w;
}
struct data
{
LL sj;
LL bh;
bool operator<(const data &b)const
{
if (this->sj == b.sj) return this->bh>b.bh;
return this->sj<b.sj;
}
};
set<struct data> preset;
hash_map<LL, LL> maps;
int n, k;
int main()
{
set<struct data>::iterator pt;
int rq = 0;
//cin>>n>>k;
n = getint();
k = getint();
LL pre;
int ml;
LL dx;
struct data mx;
for (int i = 1; i <= n; i++)
{
//scanf("%d",&ml);
ml = getint();
if (ml == 1)
{
dx = getint();
// scanf("%lld",&dx);
struct data np;
np.bh = dx; np.sj = maps[dx];
preset.erase(np);
maps[dx]++;
np.sj = maps[dx];
preset.insert(np);
team.push(dx);
if (team.size() == k + 1)
{
pre = team.front();
np.bh = pre; np.sj = maps[pre];
preset.erase(np);
team.pop();
maps[pre]--;
if (maps[pre] == 0) continue;
np.sj = maps[pre];
preset.insert(np);
}
}
if (ml == 2)
{
pt = preset.end();
--pt;//迭代器自减至set最后一个元素的位置
int bh = (*pt).bh;
// cout<<bh<<endl;
printf("%d\n", bh);
}
}
return 0;
}