This repository was archived by the owner on Sep 29, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path1346.cpp
More file actions
267 lines (238 loc) · 7.5 KB
/
1346.cpp
File metadata and controls
267 lines (238 loc) · 7.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#include <cassert>
#include <iostream>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <string>
using std::cin, std::cout, std::endl;
/// Wrapper class for the whole solution.
class Solution {
private:
enum Gender { MALE, FEMALE };
inline static const char *genderDisplayName (Gender gender) {
if (gender == MALE) return "male";
return "female";
}
struct Student {
private:
mutable bool dirty_ = true;
mutable int averageScore_;
public:
std::string name;
Gender gender;
int classNumber;
mutable int scores[9];
mutable int scoreChanges[9];
mutable int rank;
Student () {
for (int i = 0; i < 9; ++i) scoreChanges[i] = -1;
}
Student (std::string name) : name(name) {
for (int i = 0; i < 9; ++i) scoreChanges[i] = -1;
}
inline bool operator< (const Student &that) const {
return name < that.name;
}
inline void markDirty () const { dirty_ = true; }
inline bool isDirty () const { return dirty_; }
inline int getAverageScore () const {
if (Solution::solution.ignoreDirty_) {
int avg = 0;
for (int i = 0; i < 9; ++i) avg += scores[i];
return avg / 9;
}
if (dirty_) {
dirty_ = false;
averageScore_ = 0;
for (int i = 0; i < 9; ++i) {
if (scoreChanges[i] >= 0) averageScore_ += scoreChanges[i];
else averageScore_ += scores[i];
}
averageScore_ /= 9;
}
return averageScore_;
}
inline void applyChanges () const {
for (int i = 0; i < 9; ++i) {
// set to -1 to reset, because score is in [0, 100]
if (scoreChanges[i] >= 0) {
scores[i] = scoreChanges[i];
scoreChanges[i] = -1;
}
}
dirty_ = true;
}
/// functor to compare students' scores.
class scoreLessThan {
public:
inline bool operator() (const std::string &lhsName, const std::string &rhsName) const {
// FIXME: hack
// this hack was added to access value of `solution` from the functor.
const Student &lhs = Solution::solution.studentInfo_[lhsName];
const Student &rhs = Solution::solution.studentInfo_[rhsName];
if (lhs.getAverageScore() != rhs.getAverageScore()) return lhs.getAverageScore() > rhs.getAverageScore();
for (int i = 0; i < 9; i++) if (lhs.scores[i] != rhs.scores[i]) return lhs.scores[i] > rhs.scores[i];
return lhsName < rhsName;
}
};
};
/// Base class for all commands.
class BaseCommand {
public:
// static methods cannot be virtual.
static void execute (Solution &solution) {
assert(false);
};
};
/// whether we have started the scoreboard.
bool started_ = false;
/**
* FIXME: hack
* whether `Student` should ignore the dirty flag.
* This is kind of magic :-)
*
* if set to true, then `Student` would use old data to calculate average score,
* causing `Student::scoreLessThan` to behave like if no data was changed,
* therefore ensuring consistent behaviour of `std::set::erase(std::string)`.
*/
bool ignoreDirty_ = false;
/// The set of students names, ordered by score.
std::set<std::string, Student::scoreLessThan> scoreboard_;
/// Index of student name.
std::unordered_map<std::string, Student> studentInfo_;
/// The set of student names that need an update during the next flush.
std::unordered_set<std::string> namesUpdateNeeded_;
void updateRankings_ () {
int i = 0;
for (const std::string &name : scoreboard_) {
++i; // 1-based rankings
studentInfo_[name].rank = i;
}
}
class Add : public virtual BaseCommand {
private:
static constexpr char errorStarted[] = "[Error]Cannot add student now.\n";
static constexpr char errorDuplicate[] = "[Error]Add failed.\n";
public:
inline static void execute (Solution &solution) {
Student student;
char gender;
cin >> student.name >> gender >> student.classNumber;
student.gender = gender == 'M' ? MALE : FEMALE;
for (int i = 0; i < 9; ++i) cin >> student.scores[i];
if (solution.started_) {
cout << errorStarted;
return;
}
if (solution.studentInfo_.count(student.name) > 0) {
cout << errorDuplicate;
return;
}
solution.studentInfo_[student.name] = student;
}
};
class Start : public virtual BaseCommand {
public:
static void execute (Solution &solution) {
solution.started_ = true;
for (const auto &[ _, student ] : solution.studentInfo_) solution.scoreboard_.insert(student.name);
solution.updateRankings_();
}
};
class Update : public virtual BaseCommand {
private:
static constexpr char errorNotFound[] = "[Error]Update failed.\n";
public:
inline static void execute (Solution &solution) {
std::string name;
int code, score;
cin >> name >> code >> score;
if (solution.studentInfo_.count(name) == 0) {
cout << errorNotFound;
return;
}
const Student &student = solution.studentInfo_[name];
student.scoreChanges[code] = score;
student.markDirty();
solution.namesUpdateNeeded_.insert(name);
}
};
class Flush : public virtual BaseCommand {
public:
static void execute (Solution &solution) {
solution.ignoreDirty_ = true;
for (const auto &name : solution.namesUpdateNeeded_) solution.scoreboard_.erase(name);
solution.ignoreDirty_ = false;
for (const auto &name : solution.namesUpdateNeeded_) {
solution.studentInfo_[name].applyChanges();
solution.scoreboard_.insert(name);
}
solution.namesUpdateNeeded_.clear();
solution.updateRankings_();
}
};
class PrintList : public virtual BaseCommand {
public:
static void execute (Solution &solution) {
for (const std::string &name : solution.scoreboard_) {
const Student &student = solution.studentInfo_[name];
cout << student.rank << ' '
<< student.name << ' '
<< genderDisplayName(student.gender) << ' '
<< student.classNumber << ' '
<< student.getAverageScore() << '\n';
}
}
};
class Query : public virtual BaseCommand {
private:
static constexpr char errorNotFound[] = "[Error]Query failed.\n";
public:
inline static void execute (Solution &solution) {
std::string name;
cin >> name;
if (solution.studentInfo_.count(name) == 0) {
cout << errorNotFound;
return;
}
cout << "STUDENT " << name << " NOW AT RANKING " << solution.studentInfo_[name].rank << '\n';
}
};
public:
static Solution solution;
void repl () {
started_ = false;
while (true) {
std::string inputCommand;
cin >> inputCommand;
// hack here to optimize for speed.
switch (inputCommand[0]) {
case 'A': { Add::execute(*this); break; }
case 'S': { Start::execute(*this); break; }
case 'U': { Update::execute(*this); break; }
case 'F': { Flush::execute(*this); break; }
case 'P': { PrintList::execute(*this); break; }
case 'Q': { Query::execute(*this); break; }
case 'E': return;
default: {
#ifndef ONLINE_JUDGE
cout << inputCommand << endl;
#endif
assert(false);
}
}
}
}
};
Solution Solution::solution;
int main () {
#ifdef ONLINE_JUDGE
std::ios_base::sync_with_stdio(false);
cin.tie(NULL);
#endif
#ifdef DEBUG_VSCODE
freopen("1346.in", "r", stdin);
#endif
Solution::solution.repl();
return 0;
}