早起脑部SPA:打 ACM 最快乐的就是滚榜读队名了

431 阅读6分钟

题目信息

题目描述

一场ICPC 正式赛共5小时。

队伍的排名由通过题数与罚时决定。通过题数更多的队伍排名更靠前,若通过题数相同,则罚时更小的队伍排名更靠前。通过题数与罚时均相同的队伍排名相同。本题中可能出现队伍排名相同的情况,此时,认为先出现在提交记录中的队伍排名靠前。

罚时是由通过题目的时间和未通过提交的次数决定的。罚时为每一道题通过时比赛开始的分钟数之和,加上该题之前未通过提交的次数乘 20 分钟得到的。例如,某队在比赛进行 1:28:35 时通过了 G题,在此之前共有 3 次未通过的提交,则 G 题对罚时的总贡献为 88 +3 x 20 =148 分钟。

需要注意的是,仅有通过的试题的未通过提交会被计算罚时。例如,某队在1题共有 14 次未通过的提交但到比赛结束,该队都没有通过1题,则这 14 次未通过的提交不会被计算罚时。在某一题通过后,该队对这一题的任何提交(无论是否能够通过)都不会影响本题通过的结果和本题的罚时。

选手在比赛过程中可以随时提交某一道试题的代码,代码将被立即评测并返回结果 (Accepted,Time Limit Exceeded, Memory Limit Exceeded, Presentation Error,Wrong Answer,Runtime Error)。其中,评测结果 Accepted为通过,其他评测结果均为不通过。

在比赛进行的前四小时 (0:00:00 ~ 4:00:00),每支队伍的提交均会在排行榜上反映出来。比赛的最后一小时 (4:00:01 ~ 5:00:00) ,排行榜将被冻结(封榜),所有的提交在排行榜对应队伍对应试题上均显示为待判题 (提交的队伍知道评测结果)。

在比赛结束后,会进行紧张刺激的滚榜环节。滚榜嘉宾将按照封榜时的排行榜,依照从最后一名到第一名,先读出队伍队名,再按照从 A 题依次到最后一题的顺序,公布排行榜上该队"待判题”状态试题最终是否通过。

如果通过,所有队伍的排名将立即重新计算,显然,已经滚榜完成(被滚榜嘉宾念过队名,且所有待判题状态的结果都已经揭晓)的队伍排名不会有影响。若该队伍排名上升,则滚榜嘉宾立即开始下一支队伍的滚榜。因此,一支队伍的队名可能被滚榜嘉宾多次读出。

例如,某队队名为"囤题",在前四小时没有通过任何一题,封榜时排在最后一名。在封榜后,该队连续通过全部十三道题目。那么滚榜嘉宾有可能读到该队队名七八次。当然,当该队上升到第一名后,其排名不会再发生变化,即使揭晓的判题结果为通过,但其排名没有发生变化,滚榜嘉宾不会再次读出其队名。

现在给出某场ICPC 完整的提交记录,请你依次输出滚榜嘉宾念出的队名次提交记录都没有的队伍不会在排行榜上出现,也不会在滚榜中被念到队名。

输入格式

输入的第一行为三个整数 n,m,K,依次为该场ICPC 试题数、该场ICPC 队伍数、该场ICPC 提交记录数。

接下来 K 行,每行为四个空格分隔的字符串,表达一条提交记录。第一个形如 at :gy :zz,代表该记录在比赛开始 at 小时 yy 分钟 zz 秒时提交。第二个字符串为一个大写英文字母,代表试题的编号 (A,B,··)。第三个字符串为队名,保证队名不含空格。第四个字符串 (可能含有空格,但为仅出现题目描述,中的六种评测结果)为该评测记录的评测结果,具体字符串的含义见试题描述部分。

输出格式

输出若干行,为该滚榜嘉宾依次读到的队名。

示例

输入

2 2 4
0:00:01 A abc Wrong Answer
0:00:02 A abc Accepted
0:19:38 A bcd Accepted
4:18:22 B abc Accepted

输出

abc
bcd
abc

解读

在封榜前,队伍 abc 仅通过 A 题,且在第二秒的第一次正确提交之前有一次错误提交,因此罚时为 20 分钟;队伍 bcd 同样仅通过 A题,且在 0:19:38 的第一次正确提交之前没有错误提交,因此罚时为 19 分钟。

在封榜后,队伍 abc 通过了 B 题。

在滚榜环节开始,由于封榜后的提交未被揭晓,因此暂时认为队伍 abc 与 bcd 均只通过一题,且前者罚时较大,排名靠后。

依照从最后一名到第一名的原则,队伍 abc 的名字先被念到,并揭晓其在封榜后的提交的结果。其通过了B 题,因此其通过题数被更新为 2,罚时同样被更新。同时,所有队伍的排名立即被重新计算。由于此时abc 通过题目数量大于 bcd,因此其排名重新计算为第一名,而bcd 成为最后一名第二名。

这之后,队伍 bcd 的名字被念到,由于其在封榜后没有提交,因此这时所有队伍的排名没有变化,滚榜嘉宾会进行其上一名队伍的滚榜。

最后,队伍 abc 的名字被念到,滚榜结束

需要注意的是,在滚榜过程中是逐题揭晓提交。也就是说,如果一支队伍封榜后通过了多道题,在其进行滚榜过程中,只要按照从 A 题依次到最后一题的顺序,该队第一个"待判题”状态试题通过,后面的“待判题"同样暂时不会揭晓,而是立刻进行排名更新过程以及可能存在的更换另一支队伍进行滚榜的过程。

数据规模约定

  • 对于 30% 的数据,n =1;
  • 对于另外 10% 的数据,m = 1;
  • 对于100%的数据, 1<n< 20,1<m <1000,1<K <104,0<t <5,00 < yy < 60,00 < zz < 60,且当2 = 5时保证 yy = z = 00。

保证提交记录按照提交时间不降序给出,即先给出的提交记录提交时间不会晚于后给出的提交记录的提交时间,试题名称为大写字母 A ~ Z,队名均为长度不超过 50 的由小写字母组成的字符串,评测状态为试题中所给的 6 种之一。

第一轮解题

解题思路

1、 定义队伍对象,包含队名、解题数量、罚时;

2、 定义队伍排名队列,使用双向链表;

3、 循环解题记录,生成0:00:00 ~ 4:00:00的排名队列;

4、 滚榜;

5、 循环解题记录,逐条读取4:00:01 ~ 5:00:00的记录,每读取一条,如果该记录为Accepted则获取链表对象,读取前一排名节点,判断是否超越,如果超越则滚榜输出;

代码

package com.lgf.noi;

import lombok.Data;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class B3692 {

    @Data
    private static class Problem {
        /**
         * 题目名称
         */
        private String name;
        /**
         * 队伍罚时
         */
        private int penalty;
        /**
         * 开始时间
         */
        private Integer startTime;
        /**
         * 结束时间
         */
        private Integer endTime;
    }


    @Data
    private static class Team {
        /**
         * 队伍名称
         */
        private String name;
        /**
         * 队伍完成题数
         */
        private int solved;
        /**
         * 队伍罚时
         */
        private int penalty;
        /**
         * 最后的解题时间
         */
        private int finalTime;
        /**
         * 题目记录
         */
        private Map<String, Problem> problemRecord = new HashMap<>();
        /**
         * 上一名队伍
         */
        private Team previous;
        /**
         * 下一名队伍
         */
        private Team next;

        public Team(String name, Team previous) {
            this.name = name;
            this.previous = previous;
            if (previous != null && previous.getNext() == null) {
                previous.setNext(this);
            }
        }


        /**
         * 添加记录,根据记录信息调整排行榜
         *
         * @param time
         * @param answer
         * @return 是否调整了排行榜
         */
        public boolean addRecord(int time, String problem, String answer) {
            boolean isSolved = false;
            //返回结果 (Accepted,Time Limit Exceeded, Memory Limit Exceeded, Presentation Error,Wrong Answer,Runtime Error)。其中,评测结果 Accepted为通过,其他评测结果均为不通过。
            if ("Accepted".equals(answer)) {
                isSolved = true;
            }
            Problem nowProblem = problemRecord.get(problem);
            if (nowProblem == null) {
                nowProblem = new Problem();
                nowProblem.setName(problem);
                nowProblem.setStartTime(this.finalTime);
                problemRecord.put(problem, nowProblem);
            }
            if (isSolved) {
                this.solved++;
                nowProblem.setEndTime(time);
                int useTime = nowProblem.getEndTime() - nowProblem.getStartTime() + nowProblem.getPenalty();
                this.penalty += useTime;
            } else {
                if (nowProblem.getEndTime() == null) {
                    nowProblem.setPenalty(nowProblem.getPenalty() + 20 * 60);
                }
            }
            boolean flag = false;


            while (this.previous != null &&
                    ((this.solved > this.previous.getSolved()) ||
                            (this.solved == this.previous.getSolved() && this.penalty < this.previous.getPenalty()))) {
                Team temp = this.previous;
                this.previous = temp.getPrevious();
                temp.setPrevious(this);
                temp.setNext(this.next);
                this.next = temp;
                if (this.previous != null) {
                    this.previous.setNext(temp);
                }
                if (temp.getNext() != null) {
                    temp.getNext().setPrevious(temp);
                }
                flag = true;
            }
            return flag;
        }

        public static void main(String[] args) {
            Map<String, Team> teamMap = new HashMap<>();
            Team lastTeam = null;

            Scanner scanner = new Scanner(System.in);

            int n = scanner.nextInt(); // 试题数
            int m = scanner.nextInt(); // 队伍数
            int k = scanner.nextInt(); // 提交记录数

            scanner.nextLine(); // 读取换行符

            int printFlag = 0; //0:不打印;1:打印之前所有排名;2:滚榜打印

            for (int i = 0; i < k; i++) {
                String line = scanner.nextLine();
                String[] tokens = line.split(" ");
                String time = tokens[0];

                //时间 0:00:01 转换为秒数
                String[] timeTokens = time.split(":");
                int minute = Integer.parseInt(timeTokens[0]) * 60 + Integer.parseInt(timeTokens[1]);
                int second = minute * 60 + Integer.parseInt(timeTokens[2]);

                String problem = tokens[1];
                String teamName = tokens[2];
                String result = tokens[3];

                // 处理输入数据,可根据需要进行相应的操作
                Team team = teamMap.get(teamName);
                if (team == null) {
                    team = new Team(teamName, lastTeam);
                    teamMap.put(teamName, team);
                }
                if (lastTeam == null) {
                    lastTeam = team;
                }
                if (minute >= 240) {
                    printFlag = printFlag == 2 ? printFlag : 1;
                    if (printFlag == 1) {
                        Team printTeam = lastTeam;
                        System.out.println(printTeam.getName());
                        //打印之前所有排名
                        while (printTeam.getPrevious() != null) {
                            printTeam = printTeam.getPrevious();
                            System.out.println(printTeam.getName());
                        }
                        printFlag = 2;
                    }
                }
                //写入记录
                boolean flag = team.addRecord(second, problem, result);
                if (flag) {
                    if (printFlag == 2) {
                        //打印当前队伍名称
                        System.out.println(team.getName());
                    }
                }
                lastTeam = lastTeam.getNext() == null ? lastTeam : lastTeam.getNext();
            }

            scanner.close();
            if (printFlag == 0) {
                Team printTeam = lastTeam;
                //打印之前所有排名
                while (printTeam != null && printTeam.getPrevious() != null) {
                    System.out.println(printTeam.getName());
                    printTeam = printTeam.getPrevious();
                }
            }
        }
    }