HD 2025 春季联赛 2
Midum
1006
博弈 找规律
解题思路
我的思路是从最一般的开始考虑 (一般性:红宝石 > 蓝宝石 > 盒子)
- 若只有红宝石,则是一个经典的巴什博弈,当 \(r \% 4 = 0\) 时后手获胜(题目保证了三个数不都为 0)。
- 若只有若干红宝石和 1 个蓝宝石。注意到拿走蓝宝石的附加作用是增或减一个红宝石 或什么都不做。则当 \(r \% 4 = 2\) 时,若先手先拿蓝宝石,则必败,因为到达不了情况 1. 的后手必胜态;若先手不拿宝石,则后手可以一直维持 \(r \% 4 = 2\) 直到只剩两个红宝石,此时无论如何都是后手必胜的。当 \(r \% 4 \not = 2\) 时,先手总有办法通过拿蓝宝石使下一个状态是后手必胜态。总而言之,当 \(r \%4 = 2\) 时后手必胜。
根据这个思路分析下去可以发现当没有盒子时,后手必胜有两种情况:
- \(r \% 4 = 0\) 且 \(b \% 2 = 0\)
- \(r \% 4 = 2\) 且 \(b \% 2 = 1\)
我们再考虑盒子,从 1 开始添加盒子,我们会发现盒子不影响以上结论。
so
CODE
bool solve()
{
int r = 0, b = 0, m = 0;
std::cin >> r >> b >> m;
if (b % 2 == 0) {
return r % 4 == 0;
}
else {
return r % 4 == 2;
}
}
1007
dp 状态压缩
解题思路
首先要知道项:
- 一个数是一个项
- 由乘法连接两个项得到的也是一个项
因为我们在图上走动向表达式添加符号或数字时,只会影响到最后一项,所以我们需要把最后一项和其他的项分开来看,对于其他项维护它们的和就好了;对于最后一项我们仍需要特殊考虑:假设最后一项是 \(A \times a\)(\(A\) 是一个项, \(a\) 是一个数字),若我们在表达式末尾添加一个数字 \(b\),则最后一项就变成了 \(A \times (10a + b)\),所以对于最后一项我们要把它拆成 \(A\) 和 \(a\) 来看。所以对于一个表达式,我们只用维护以下信息 \((sum, mul, cur)\)。其中 \(sum\) 是最后一个项之外的项之和;\(mul\) 是上面我们提到的 \(A\) 的值,即最后一个项中,最后一个数之外的数的乘积;\(cur\) 是上面我们提到的 \(a\),即最后一个项中的最后一个数。
于是每当往表达式后面加字符时,表达式的变化可以看成 \((sum, mul, cur) -> (sum^{'}, mul^{'}, cur^{'})\)。具体的:
- 字符是数字 \(x\):\(sum^{'} = sum, mul^{'} = mul, cur^{'} = 10cur + x\)
- 字符是加号 \(+\):\(sum^{'} = sum + mul \times cur, mul = 1, cur = 0\)。即原先的最后一项在新的表达式中是其他项。
- 字符是减号 \(-\):\(sum^{'} = sum + mul \times cur, mul^{'} = -1, cur^{'} = 0\)。即原先的最后一项在新的表达式中是其他项。
- 字符是乘号 \(*\):\(sum^{'} = sum, mul^{'} = mul \times cur, cur^{'} = 0\)。
我们将 \((sum, mul, cur)\) 看作一个状态,则只有 \(20^3\) 种可能,是可以压缩成整数的。再将字符看作是状态的转移,具体的转移上面已经给出了。则我们可以维护出每个状态的次态:
int hash(int k, int sum, int mul, int cur) {
return (sum * k + mul) * k + cur;
}
void solve() {
// code ...
for (int sum = 0; sum < k; sum++) {
for (int mul = 0; mul < k; mul++) {
for (int cur = 0; cur < k; cur++) {
for (char d = '0'; d <= '9'; d++) {
trans[d][hash(k, sum, mul, cur)] = hash(k, sum, mul, (cur * 10 + d - '0') % k);
}
trans['+'][hash(k, sum, mul, cur)] = hash(k, (sum + mul * cur) % k, 1, 0);
trans['-'][hash(k, sum, mul, cur)] = hash(k, (sum + mul * cur) % k, k - 1, 0);
trans['*'][hash(k, sum, mul, cur)] = hash(k, sum, (mul * cur) % k, 0);
}
}
}
// code ...
}
有了 trans 我们就可以优雅地 dp 了:
CODE
constexpr int N = 100, K = 20 * 20 * 20;
int f[N + 5][N + 5][K + 5];
int trans[128][K + 5];
int hash(int k, int sum, int mul, int cur) {
return (sum * k + mul) * k + cur;
}
void solve() {
int n = 0, m = 0, k = 0;
std::cin >> n >> m >> k;
for (int i = 0; i < n; i++) {
for (int j = 0; j <= m; j++) {
for (int s = 0; s < k * k * k; s++) {
f[i][j][k] = 0;
}
}
}
for (int sum = 0; sum < k; sum++) {
for (int mul = 0; mul < k; mul++) {
for (int cur = 0; cur < k; cur++) {
for (char d = '0'; d <= '9'; d++) {
trans[d][hash(k, sum, mul, cur)] = hash(k, sum, mul, (cur * 10 + d - '0') % k);
}
trans['+'][hash(k, sum, mul, cur)] = hash(k, (sum + mul * cur) % k, 1, 0);
trans['-'][hash(k, sum, mul, cur)] = hash(k, (sum + mul * cur) % k, k - 1, 0);
trans['*'][hash(k, sum, mul, cur)] = hash(k, sum, (mul * cur) % k, 0);
}
}
}
std::vector g(n, std::string{});
for (int i = 0; i < n; i++) {
std::cin >> g[i];
g[i].push_back('+');
}
f[0][0][hash(k, 0, 1, (g[0][0] - '0') % k)] = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
char c = g[i][j];
for (int s = 0; s < k * k * k; s++) {
if (f[i][j][s] == 0) {
continue;
}
if (i + 1 < n && (std::isdigit(c) || std::isdigit(g[i + 1][j]))) {
(f[i + 1][j][trans[g[i + 1][j]][s]] += f[i][j][s]) %= Mod;
}
if (std::isdigit(c) || std::isdigit(g[i][j + 1])) {
(f[i][j + 1][trans[g[i][j + 1]][s]] += f[i][j][s]) %= Mod;
}
}
}
}
std::cout << f[n - 1][m][hash(k, 0, 1, 0)] << '\n';
return;
}
1008
拓扑 数学?(本题考查了如何根据题意建图)
解题思路
对于每个三角形,最终会有三把刷子经过它,具体是哪些刷子由三角形的坐标决定。
可以作为初步判断的依据是:每个小三角形最终的颜色是否来自它对应的三把刷子之一。不是就直接判 No 就好了。
否则就建图,对于上了色的小三角形,其他两个颜色连向最终的颜色。最后判断有没有环就好了。有就判 No。
CODE
constexpr int N = 3e5, MAX = 1e4;
std::vector<int> g[N * 3 + 1];
int in[N * 3 + 1];
bool solve() {
int n = 0, m = 0;
std::cin >> n >> m;
// init
for (int i = 1; i <= 3 * n; i++) {
in[i] = 0;
g[i].clear();
}
bool ok = true;
for (int i = 0; i < m; i++) {
int x = 0, y = 0, col = 0;
std::cin >> x >> y >> col;
int c1 = y + 1 >> 1, c2 = 2 * n + 1 - x, c3 = (2 * x - y + 1 >> 1) + 2 * n;
// std::cout << c1 << ' ' << c2 << ' ' << c3 << '\n';
if (col != c1 && col != c2 && col != c3) {
ok = false;
}
if (ok) {
if (col != c1) {
g[c1].push_back(col);
}
if (col != c2) {
g[c2].push_back(col);
}
if (col != c3) {
g[c3].push_back(col);
}
}
}
if (not ok) {
return false;
}
for (int i = 1; i <= 3 * n; i++) {
if (g[i].empty()) {
continue;
}
std::sort(g[i].begin(), g[i].end());
g[i].erase(std::unique(g[i].begin(), g[i].end()), g[i].end());
for (int j = 0; j < int(g[i].size()); j++) {
in[g[i][j]]++;
}
}
std::queue<int> q;
for (int i = 1; i <= 3 * n; i++) {
if (in[i] == 0 && not g[i].empty()) {
q.push(i);
}
}
while (not q.empty()) {
int cur = q.front();
q.pop();
for (auto &to : g[cur]) {
if (--in[to] == 0) {
q.push(to);
}
}
}
for (int i = 1; i <= 3 * n; i++) {
if (in[i] != 0) {
return false;
}
}
return true;
}
浙公网安备 33010602011771号