HD 2025 春季联赛 2

Midum

1006

博弈 找规律

解题思路

我的思路是从最一般的开始考虑 (一般性:红宝石 > 蓝宝石 > 盒子)

  1. 若只有红宝石,则是一个经典的巴什博弈,当 \(r \% 4 = 0\) 时后手获胜(题目保证了三个数不都为 0)。
  2. 若只有若干红宝石和 1 个蓝宝石。注意到拿走蓝宝石的附加作用是增或减一个红宝石 或什么都不做。则当 \(r \% 4 = 2\) 时,若先手先拿蓝宝石,则必败,因为到达不了情况 1. 的后手必胜态;若先手不拿宝石,则后手可以一直维持 \(r \% 4 = 2\) 直到只剩两个红宝石,此时无论如何都是后手必胜的。当 \(r \% 4 \not = 2\) 时,先手总有办法通过拿蓝宝石使下一个状态是后手必胜态。总而言之,当 \(r \%4 = 2\) 时后手必胜。

根据这个思路分析下去可以发现当没有盒子时,后手必胜有两种情况:

  1. \(r \% 4 = 0\)\(b \% 2 = 0\)
  2. \(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 状态压缩

解题思路

首先要知道项:

  1. 一个数是一个项
  2. 由乘法连接两个项得到的也是一个项

因为我们在图上走动向表达式添加符号或数字时,只会影响到最后一项,所以我们需要把最后一项和其他的项分开来看,对于其他项维护它们的和就好了;对于最后一项我们仍需要特殊考虑:假设最后一项是 \(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^{'})\)。具体的:

  1. 字符是数字 \(x\)\(sum^{'} = sum, mul^{'} = mul, cur^{'} = 10cur + x\)
  2. 字符是加号 \(+\)\(sum^{'} = sum + mul \times cur, mul = 1, cur = 0\)。即原先的最后一项在新的表达式中是其他项。
  3. 字符是减号 \(-\)\(sum^{'} = sum + mul \times cur, mul^{'} = -1, cur^{'} = 0\)。即原先的最后一项在新的表达式中是其他项。
  4. 字符是乘号 \(*\)\(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;
}
posted @ 2025-03-15 23:23  Young_Cloud  阅读(11)  评论(0)    收藏  举报