- 题解
C. AtCoder 排名模拟
- @ 2026-7-18 1:12:15

思路:
- 计分规则 共 M 道题(M<=20),每题分数Sj;只有首次 AC 才能拿到该题满分,后续提交无效。 总得分 = 所有 AC 题目分数相加。
- 单题罚时规则仅对成功 AC的题目计算罚时: 单题罚时 = 首次AC时刻 + AC前非CE错误提交次数*300 错误类型:WA/TLE/RE → 计入错误次数; CE(编译错误):完全忽略,不增加错误次数、不产生任何罚时; 未 AC 的题目:无罚时。
- 总罚时规则总罚时 = 该选手所有 AC 题目单题罚时的最大值(不是累加!这是本题最容易踩坑的点); 如果一道题都没 AC,总罚时为 0.
- 排序 & 排名规则排序优先级(从高到低): 总得分:越大越靠前; 总罚时:得分相同时,越小越靠前; 选手编号:得分、总罚时全相同,编号小的排在前面(并列名次)。 并列名次判定: 两名选手总得分、总罚时完全相等 → 同一名次; 名次值 = 该分数 + 罚时组合第一次出现的行号(从 1 开始计数)。
- 输入输出特殊要求 输入:多组数据,提交记录按时间非递减给出; 输出:每组输出 N 行;不同测试组之间用一个空行隔开。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct students{
int id;
int question;
int time;
string answer;
}Stu[500005];
struct ans{
int id;
int question;
int time=0;
int time1[22]={0};
int count[22]={0};
bool visted[22]={0};
}Ans[200005];
int num[20];
//结构体排序
bool cmp1(ans a,ans b){
if(a.question!=b.question){
return a.question>b.question;
}
if(a.time!=b.time){
return a.time<b.time;
}
return a.id<b.id;
}
signed main()
{
int t;
cin>>t;
while(t--){
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=m;i++){
cin>>num[i];
}
//初始化
for(int i=1;i<=n;i++){
Ans[i].id=i;
Ans[i].time=0;
Ans[i].question=0;
memset(Ans[i].time1,0,sizeof(Ans[i].time1));
memset(Ans[i].count,0,sizeof(Ans[i].count));
memset(Ans[i].visted,0,sizeof(Ans[i].visted));
}
for(int i=0;i<k;i++){
cin>>Stu[i].id>>Stu[i].question>>Stu[i].time>>Stu[i].answer;
}
for(int i=0;i<k;i++){
int id1=Stu[i].id;
int x=Stu[i].question;
string ans1=Stu[i].answer;
if(Ans[id1].visted[x]){
continue;
}
//核心计算当题目AC时的罚时和得分
if(ans1=="AC"){
Ans[id1].time1[x]=Stu[i].time+Ans[id1].count[x]*300;
Ans[id1].time=max(Ans[id1].time,Ans[id1].time1[x]);
Ans[id1].question+=num[x];
Ans[id1].visted[x]=1;
}
//记录除了AC和CE情况的次数
else if(ans1!="CE"){
Ans[id1].count[x]++;
}
}
sort(Ans+1,Ans+1+n,cmp1);
//计算名次
int d=1;
for(int i=1;i<=n;i++){
if(i>1){
if(!(Ans[i].question==Ans[i-1].question&&Ans[i].time==Ans[i-1].time)){
d=i;
}
}
cout<<d<<' '<<Ans[i].id<<' '<<Ans[i].question<<' '<<Ans[i].time<<endl;
}
cout<<endl;
}
}
0 条评论
目前还没有评论...