点击前往试题目录:https://blog.youkuaiyun.com/best335/article/details/99550556
题意:
有m∈[0,49]类商品,每类商品有n∈[0,30000]个物品.
初始时,给出第i<n个商品的ID和Score,即初始时所有类商品的第i个物品的ID和Score都相同。
有三种操作:
- 增加第type类编号为commodity分数为score的商品
- 删除第type类编号为commodity的商品
- 查询分数在前K个商品且每类商品个数不超过k_i
其中总操作个数OPnum <=100000,查询个数OPask<=100
求每次查询时每类商品选出的商品编号:
- 相同分数的选择类别更小的物品
- 类别相同分数也相同选择ID更小的物品
- 该类若无选择输出-1,否则按编号升序输出选择的物品,每类输出占一行,共输出OPask*m行,即输出所有查询操作所对应的每类选择的升序商品编号。
考点:问题需求理解能力(倒不如说出题人的表达能力)、STL的熟练应用、时间复杂度的分析、问题建模能力。
题目未描述的部分:
- 增加的商品编号已存在如何处理(假定数据不会输入重复类目编号)
解题思路
- 使用map<int,set<pair<int,int> > > F保存所有数据,格式为map<Score,set<pair<Type,Commodity> > >
这样在容器内部首先会按Score排序,同一Score的先按Type排序,再按Commodity排序。 - 使用unordered_map<int,int>[50]保存编号到成绩的映射,格式为
unordered_map<Commodity,Score> G[Type]
这样指定Type和Commodity可以在F中迅速定位,从F中增删商品的时间度为O(log(Score的种数) * log(该分数的物品数))。
基础知识
- map<class T1,class T2>特性:存储T1->T2映射的键值对,map先按照T1升序排序,再按T2升序排序,其中T1,T2可以是任意类(如:int、string、char或自定义类),T1值唯一,其插入删除的时间复杂度为O(log2)
- unordered_map<class T1,class T2>特性:仅存储T1->T2映射的键值对,T1值唯一,其插入和删除的时间复杂度为O(1)
60分代码 2531ms/5000ms(疑似测试样例BUG,至今也没有想出到底是哪块出了问题。)
#pragma GCC optimize(2)
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<unordered_map>
#define DEBUG
#ifdef DEBUG
#include<fstream>
#include<ctime>
#endif
using namespace std;
int main(){
#ifdef DEBUG
clock_t t1=clock();
ifstream cin("C:\\Users\\Isidore\\Desktop\\out.txt");
#endif
int m,n,op;
ios_base::sync_with_stdio(false),cin.tie(NULL);
unordered_map<int,int> G[51];//存储编号到分数的映射
unordered_map<int,int>::iterator itG;
map<int,set<pair<int,int> > > F;//map<Score,set<pair<Type,Commodity> > >
map<int,set<pair<int,int> > >::iterator itF;
cin>>m>>n;//初始化商品
for(int i=0,id,score;i<n;++i){
cin>>id>>score;
for(int j=0;j<m;++j) F[score].insert(pair<int,int>(j,id)),G[j][id]=score;
}
cin>>op;//增删查操作
for(int i=0,k,kind,type,commodity,score,K[51];i<op;++i){
cin>>kind;
switch(kind){
case 1://增加
cin>>type>>commodity>>score;
G[type][commodity]=score;//在G中记录该Type类编号为Commodity商品成绩映射
F[score].insert(pair<int,int>(type,commodity));//在F中插入该商品
break;
case 2://删除
cin>>type>>commodity;
if((itG=G[type].find(commodity))!=G[type].end()){//如果该商品存在则开始删除
itF=F.find(itG->second);//首先查找该商品在F中找到所有Score为itG->second的所有商品
itF->second.erase(pair<int,int>(type,commodity));//删除该商品
if(itF->second.empty())F.erase(itF);//如果该分数的容器为空 删掉
G[type].erase(itG);//删掉Type类的Commodity到Score的映射
}
break;
case 3://查询
cin>>k;//查询总个数k
for(int j=0;j<m;++j) cin>>K[j];//输入每类不得超过的最大查询数
set<int> S[51];//存储所有类查询到的商品编号
for(auto it=F.rbegin();it!=F.rend()&&k>0;++it){//按分数由大到小选择前k个物品
set<pair<int,int> >&s=it->second;//该分数下所有商品
for(auto itS=s.begin();itS!=s.end()&&k>0;++itS){
const pair<int,int>&p=*itS;
if(K[p.first]>0) S[p.first].insert(p.second),--K[p.first],--k;//若该物品不超过选择数量则选择该物品
}
}
for(int j=0;j<m;++j){//对所有类输出选择的物品
if(S[j].empty())//该类没有选中任何物品
cout<<-1<<endl;
else{
k=0;//仅仅用于判断是否输出空格
for(auto it=S[j].begin();it!=S[j].end();++it,++k)
cout<<(k==0?"":" ")<<*it;
cout<<endl;
}
}
break;
}
}
#ifdef DEBUG
clock_t t2=clock();
::cout<<endl<<endl<<t2-t1<<"ms"<<endl;
#endif
return 0;
}
测试数据
//样例数据
2 3
1 3
2 2
3 1
8
3 100 1 1
1 0 4 3
1 0 5 1
3 10 2 2
3 10 1 1
2 0 1
3 2 1 1
3 1 1 1
//自测随机数据(没有灵魂)
#include<iostream>
#include<fstream>
#include<stdlib.h>
#include<map>
#include<time.h>
#include<set>
#include<iomanip>
#include<unordered_map>
#include<unordered_set>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
void output190904(){
ofstream cout("out.txt");
srand( time(NULL) );
unordered_set<int> ids[50];
int m=50,n=30000,id[30001],opnum=100000,oppask=100,kind;
cout<<m<<" "<<n<<endl;
for(int i=0;i<n;++i){
id[i]=60000,cout<<i+i<<" "<<abs(rand()*rand())<<endl;
for(int j=0;j<50;++j)ids[j].insert(i+i);
}
cout<<opnum<<endl;
for(int i=0;i<opnum;++i){
switch(rand()%3){
case 0:
kind=rand()%50;
cout<<1<<" "<<kind<<" "<<(id[kind]+=2)<<" "<<abs(rand()*rand())<<endl;
break;
case 1:
do{kind=rand()%50;}while(ids[kind].empty());
cout<<2<<" "<<kind<<" "<<*ids[kind].begin()<<endl;
ids[kind].erase(ids[kind].begin());
break;
case 2:
if(oppask>1){
--oppask;
cout<<3<<" "<<rand()%101;
for(int j=0;j<m;++j)
cout<<" "<<(rand()%100001);
cout<<endl;
}
else
--i;
break;
}
}
cout<<3<<" "<<rand()%101<<" ";
for(int j=0;j<m;++j) cout<<" "<<rand()%100001;
}
int main(){
output190904();
return 0;
}