题目:ACM Rank
题意:其实搞ACM的题意都知道是干嘛的啦。给定n个队伍,然后m道题。跟着q个操作,要么是队伍提交,要么是查询。
每次没有AC的罚时是20分钟,相同解题数相同罚时的队伍排名相同,可以看题目里面的表。剩下的排名跟罚时计算规则跟平常的比赛一样,我就不多说了。
1、S [minute]:[Team No]:[Problem ID]:[Result]
表示在minute分钟时,编号为Team No的队伍提交了第ID题,评测结果为Result。
首先,如果该队伍已经AC了这道题了,那么忽略这个提交,此次为无效提交。
如果该队伍上一次有效提交(不管提交了哪道题)跟此次提交的事件差距小于5分钟,那么忽略这个提交,即视此次为无效提交。
对于合法提交,Result值为1表示AC,否则没有AC。如果AC了,还要输出[Team No][Problem ID]。
2、R [Team No]
查询编号为Team No的队伍的排名
3、T [k]
查询排名为k的队伍的编号。如果有多个队伍并列,那么取最早AC最后一题的队伍。如果都是0AC的,输出最小编号。
如果不存在排名k的,输出-1。比如题目给的图片就没有4、6、9等等。
题意很好懂,就是怎么实现。如果每次都去重新排序,10000个队伍+100000次查询更新是行不通的。
由于题目最多是10题,所以可以开按照解题数开10个树状数组。每个数组里面按照罚时来存,比如说某个队伍当前AC了1题,罚时为30分钟,那么就在第1个树状数组的30这个位置加1。如果它再AC一题,就先把这个30的1减掉,然后更新到第2个树状数组的对应罚时位置。
那么罚时最多是多少呢?粗略算的,10道题都在300分钟AC(虽然这不可能),由于5分钟才能交一次,即使0分钟开始交最多也就交61次,所以罚时数不超过3000+1206<5000,当然你还可以算的更精细些。我就按着5000来开了。
对于查询某个队伍的排名。我们可以用sum[i]表示当前解出i道题的队伍的数量,那么根据当前队伍的解题数x,先把解题数比它多的加起来,由于最多10个题,暴力算也相当于常数操作。跟着在第x个树状数组里面,求出比它罚时少的队伍数,全部加起来再加上1就是当前队伍的排名。
sol_num表示至少解出1题的队伍数。
solved[id]表示队伍的解题数。
int R(){
int id, res;
in(id);
if(solved[id]==0) return sol_num+1;
res = cal(solved[id], pen[id]-1)+1;
for(int i=solved[id]+1; i<=m; i++) res+=sum[i];
return res;
}
比较麻烦是第3个查询。
如果k大于队伍数,输出-1。
如果k大于sol_num,那么解题数为0的排名都是sol_num+1,如果k等于sol_num+1,就输出还未AC的队伍的最小编号,否则还是输出-1。
排除上面的,我们可以确定排名k的队伍是某个有解题的队伍,但不一定存在k这个排名,有可能出现并列覆盖掉k的。
我们可以先枚举解题数,找到排名k的队伍的解题数i。
然后在第i个树状数组二分出排名k的罚时x。我们可以利用第二个查询的方法,求出解出i道题罚时为x的实际排名。
如果这个排名就是k,那么我们就输出解出i道题,罚时为x的,最早AC最后一题的队伍编号,否则输出-1。
至于这个队伍的求法,我是用map<int, set<Team> >,对应解题数开10个map,每个map都是罚时到队伍集合的映射。即根据解题数跟罚时可以找到这个队伍集合,集合里面的队伍按照AC最后一题的先后排序,那么取最左边的值即可。
剩下一些维护的工作就是要维护好map和树状数组。一个队伍如果成功AC一道题,记得先把它原先在树状数组的位置去除,从原先所在的map集合里面删除,然后添加到新的位置去。
#include<cstdio>
#include<cstring>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int N = 100100;
const int M = 5000;
int n, m, sol_num, tot;
int arr[11][M];//树状数组
int cnt[N][11];//每个队伍每道题需要记录的WA的次数
int sum[11];//解出i道题的队伍数
bool solve[N][10];//solve[i][j]表示i队是否解出第j题
char s1[20], s2[20];
int solved[N];//队伍的解题数
int pen[N];//队伍的罚时
int last[N];//队伍最近一次有效提交的事件
struct Team{
int id, ac;//ac代表最后一次ac,在查询里面的编号
Team(){}
Team(int id, int ac):id(id),ac(ac){}
bool operator < (const Team &A)const{
return ac<A.ac;
}
};
int lowbit(int x){
return x&(-x);
}
void add(int sol, int x, int v){
if(x==0){//有可能出现罚时为0的情况
arr[sol][x]+=v;
return;
}
for(; x<M; x+=lowbit(x)) arr[sol][x]+=v;
}
int cal(int sol, int x){
if(x<0) return 0;
int res = arr[sol][0];//有可能出现罚时为0的情况
for(; x>0; x-=lowbit(x)) res += arr[sol][x];
return res;
}
Team t[N];
map<int, set<Team> > MP[11];
set<int> ST;//用来存放AC数为0的队伍编号。
void init(){
for(int i=1; i<=m; i++){
MP[i].clear();
}
ST.clear();
for(int i=0; i<n; i++){
ST.insert(i);
t[i] = Team(i,-10);
last[i] = -10;
}
tot = 0;
sol_num = 0;
memset(solved, 0, sizeof(solved));
memset(pen, 0, sizeof(pen));
memset(arr, 0, sizeof(arr));
memset(solve, 0, sizeof(solve));
memset(cnt, 0, sizeof(cnt));
memset(sum, 0, sizeof(sum));
}
inline void in(int &x){
char c=getchar();
x = 0;
while(c<48 || c>57) c=getchar();
while(c>=48 && c<=57){
x = x*10+c-48;
c = getchar();
}
}
inline void read(int &x){
char c=getchar();
x = c-'A';
}
void submit(){
int min, id, flag, pro;
in(min);
in(id);
read(pro);
in(flag);
if(min - last[id] < 5 || solve[id][pro]) return;
last[id] = min;
if(flag){
printf("[%d][%c]\n", id, pro+'A');
int sol = solved[id];
int penlty = pen[id];
if(sol){
MP[sol][penlty].erase(t[id]);
add(sol, penlty, -1);
sum[sol]--;
}
else{
sol_num++;
ST.erase(id);
}
sol++;
penlty += min + cnt[id][pro]*20;
solved[id]++;
pen[id] = penlty;
t[id].ac = ++tot;
MP[sol][penlty].insert(t[id]);
add(sol, penlty, 1);
solve[id][pro]=1;
sum[sol]++;
}
else{
cnt[id][pro]++;
}
}
int R(){
int id, res;
in(id);
if(solved[id]==0) return sol_num+1;
res = cal(solved[id], pen[id]-1)+1;
for(int i=solved[id]+1; i<=m; i++) res+=sum[i];
return res;
}
int find(int sol, int rank){
int low=0, top=M, mid;
int ans = M;
while(low<=top){
mid = (low+top)>>1;
int res = cal(sol, mid);
if(res >= rank){
ans = min(ans, mid);
top = mid-1;
}
else{
low = mid+1;
}
}
int tmp = cal(sol, ans-1);
if(tmp + 1 != rank) return -1;
return MP[sol][ans].begin()->id;
}
int T(){
int rank;
in(rank);
if(rank > n) return -1;
if(rank == sol_num+1) return *ST.begin();
if(rank > sol_num) return -1;
int i, res = 0;
for(i=m; i>=0; i--){
res += sum[i];
if(res >= rank) break;
}
res -= sum[i];
return find(i, rank - res);
}
int main(){
while(~scanf("%d %d", &n, &m)){
init();
while(~scanf("%s", s1) && s1[0]!='C'){
if(s1[0]=='S') submit();
else if(s1[0]=='T') printf("%d\n", T());
else printf("%d\n", R());
}
scanf("%s", s1);
puts("");
}
return 0;
}