problem1 link
设$f[i][j][k]$表示考虑了前$i$道题,剩下时间为$j$,剩下技能为$k$的最大得分.
从小到大计算二元组$(j,k)$的话,在存储上可以省略掉$i$这一维.
problem2 link
首先,不同的提交状态有8种.预计算每一种提交状态的每一个分值的种数,设为$g[mask][score]$.对于$g[mask][score]$的计算,可以枚举$mask$包含的一个题目,设为$i$,其分值为$p_{i}$,那么第$i$道题得分如果为$x$,那么其他$mask^{‘}=mask$^$2^{i}$题目得分为$score-x$,所以$g[mask][score]=\sum_{x=1}^{p_{i}}g[mask^{‘}][score-x]$
最后从前向后考虑每个人的得分.设$f[i][score]$表示前$i$个人的得分降序排列且第$i$个人得分为$score$的方案数.设第$i+1$个人的得分为$y$,由于第$i$个人的得分一定大于$y$,所以$f[i+1][y]=\sum_{x=y+1}^{MaxScore_{i}}f[i][x]$
由于计算数组$g$和$f$都是连续求和,可以通过预处理前缀和或者后缀和来加速记算
problem3 link
code for problem1
#include <iostream> #include <vector> #include <algorithm> using namespace std; class SRMCodingPhase { public: int countScore(vector <int> points, vector <int> skills, int luck) { vector<vector<int>> f(76, vector<int>(luck + 1, -1)); f[75][luck] = 0; const int b[] = {2, 4, 8}; for (int i = 0; i < 3; ++ i) { const int point = points[i]; const int skill = skills[i]; for (int j = 0; j <= 75; ++ j) { for (int k = 0; k <= luck; ++ k) { if (f[j][k] == -1) { continue; } for (int t = 0; t <= k && t < skill; ++ t) { const int last_point = point - b[i] * (skill - t); if (last_point <= 0) { continue; } if (skill - t > j) { continue; } f[j - (skill - t)][k - t] = max(f[j - (skill - t)][k - t], last_point + f[j][k]); } } } } int result = 0; for (int j = 0; j <= 75; ++ j) { for (int k = 0; k <= luck; ++ k) { result = max(result, f[j][k]); } } return result; } };
code for problem2
#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; #define mod 1000000007 #define MAX_SCORE 200000 int g[8][MAX_SCORE + 1]; int f[20][MAX_SCORE + 1]; class SRMIntermissionPhase { public: int countWays(vector<int> points, vector<string> description) { Initialize(points); const int mask0 = GetMask(description[0]); for (int i = 0; i <= MAX_SCORE; ++ i) { f[0][i] = GetRangeSum(mask0, i, i); } CalculateSuffixSum(0); const int person_number = (int)description.size(); for (int i = 1; i < person_number; ++ i) { const int mask = GetMask(description[i]); for (int j = 0; j < MAX_SCORE; ++ j) { f[i][j] = (long long)GetRangeSum(mask, j, j) * f[i - 1][j + 1] % mod; } CalculateSuffixSum(i); } return f[person_number - 1][0]; } private: void CalculateSuffixSum(int idx) { for (int i = MAX_SCORE - 1; i >= 0; -- i) { Add(f[idx][i], f[idx][i + 1]); } } void CalculatePrefixSum(int idx) { for (int i = 1; i <= MAX_SCORE; ++ i) { Add(g[idx][i], g[idx][i - 1]); } } int GetMask(const std::string& s) { int mask = 0; for (int i = 0; i < 3; ++ i) { if (s[i] == ‘Y‘) { mask |= 1 << i; } } return mask; } void Initialize(const vector<int>& points) { g[0][0] = 1; CalculatePrefixSum(0); for (int i = 1; i < 8; ++ i) { int low_bit = 0; for (int j = 0; j < 3; ++ j) { if ((i & (1 << j)) != 0) { low_bit = j; break; } } const int other = i ^ (1 << low_bit); const int point = points[low_bit]; for (int k = 1; k <= MAX_SCORE; ++ k) { g[i][k] = GetRangeSum(other, k - point, k - 1); } CalculatePrefixSum(i); } } int GetRangeSum(int mask, int left, int right) { if (left <= 0) { return g[mask][right]; } int result = g[mask][right] - g[mask][left - 1]; if (result < 0) { result += mod; } return result; } void Add(int &x, int y) { x += y; if (x >= mod) { x -= mod; } } };
code for problem3