Educational Codeforces Round 8

A

模拟即可

int n, b, p;
    std::cin >> n >> b >> p;
    const int N = n;

    ll ans = 0;
    while (n > 1) {
        int k = bit_floor(n);
        ans += k / 2 * (b * 2 + 1);
        n -= k / 2;
    }

    std::cout << ans << " " << N * p << "\n";

B

一个数字字符串中有多少能被$4$整除的字串,由于$100$是$4$的倍数,只要考虑低两位是不是$4$的倍数,和一位是$4$的倍数的情况

    std::string s;
    std::cin >> s;

    const int N = s.size();
    std::vector<int> a;
    for (auto c : s) {
        a.push_back(c - '0');
    }

    ll ans = 0;
    for (int i = 0; i < N; i++) {
        ans += a[i] % 4 == 0;
        if (i > 0 && (a[i] + a[i - 1] * 10) % 4 == 0) {
            ans += i;
        }
    }

    std::cout << ans << "\n";

C

能否构造一个小写字符串,使得到另外一个给定小写字符串的距离为给定值,距离的定义是相同长度的两个小写字符串的对应位置的字符ascii码差的绝对值之和。

考虑贪心,每次取最大差,因为满足条件后所有剩下的位都可以取$0$

    int n, k;
    std::string s;
    std::cin >> n >> k >> s;

    std::string ans;
    for (auto c : s) {
        int a = std::abs('a' - c), z = std::abs('z' - c);
        int d = std::min(k, std::max(a, z));
        k -= d;
        ans.push_back(islower(c + d) ? c + d : c - d);
    }

    std::cout << (k ? "-1" : ans) << "\n";

D

给定$[a,b]$,求其中有多少满足能被$m$整除且偶数位上为$d$,奇数位上不为$d$的数。

考虑数位dp,令$dp[i][j][k]$表示当前枚举到第$i$位,余数是$j$,限制为$k$的满足条件的数的个数($k=0$表示无限制,$k=1$表示有限制)

转移如下:

假设下一位选$b$,奇数位只能选非$d%$且在范围内的数或没有方案,偶数位只能选$d$或没有方案

$$
dp[i+1][(j*10+b)%m][0]+=
\begin{cases}
dp[i][j][0] \text{ any case, ok}\
dp[i][j][1] \text{ if the top is not reached}
\end{cases}
$$

由于这里位数给的很长,而且$d=0,a=1$的时候会有问题,所以在最后特判一下$a$是否能选进去,复杂度$O(nm)$,$n$是数字长度

    int m, d;
    std::cin >> m >> d;

    std::string a, b;
    std::cin >> a >> b;

    std::for_each(a.begin(), a.end(), [](auto &c) { c -= '0'; });
    std::for_each(b.begin(), b.end(), [](auto &c) { c -= '0'; });

    Z ans = 0;
    auto work = [&](const std::string s) {
        const int N = s.size();
        std::vector dp(N + 1, std::vector(m, std::vector<Z>(2)));
        dp[0][0][1] = 1;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < m; j++) {
                for (int k = 0; k < 2; k++) {
                    const int end = k ? s[i] : 9;
                    for (int b = 0; b <= end; b++) {
                        if ((i % 2 == 0 && b == d) || (i % 2 && b != d)) {
                            continue;
                        }
                        dp[i + 1][(j * 10 + b) % m][k && s[i] == b] +=
                            dp[i][j][k];
                    }
                }
            }
        }
        return dp[N][0][0] + dp[N][0][1];
    };

    ans += work(b) - work(a);

    bool ok = true;
    int r = 0;
    for (int i = 0; i < a.size(); i++) {
        if (i & 1) {
            ok &= a[i] == d;
        } else {
            ok &= a[i] != d;
        }

        r = (r * 10 + a[i]) % m;
    }

    if (ok && r == 0) {
        ans++;
    }

    std::cout << ans << " ";

E

枚举拐点,之前不会学了一下,这次还是不会😕

预处理:

  • ld:往左下角最多延伸多少个z
  • l:往左最多延伸多少个z
  • r:往右最多延伸多少个z

每个反对角线单独统计

右上角拐点要形成z-pattern的限制是ld和l

左下角拐点要形成z-pattern的限制是r

考虑每次把左下角的限制转化为时间维度,因为对于每个右上角拐点不会统计超过当前横坐标的值(可以理解为这也是一种限制),用树状数组来维护就好了

    int n, m;
    std::cin >> n >> m;

    std::vector<std::string> s(n);

    for (int i = 0; i < n; i++) {
        std::cin >> s[i];
    }

    std::vector ld(n, std::vector<int>(m)), l(n, std::vector<int>(m)),
        r(n, std::vector<int>(m));
    for (int i = n - 1; i >= 0; i--) {
        for (int j = 0; j < m; j++) {
            if (i < n - 1 && j > 0 && s[i][j] == 'z') {
                ld[i][j] = ld[i + 1][j - 1] + 1;
            } else if (s[i][j] == 'z') {
                ld[i][j] = 1;
            }

            if (s[i][j] == 'z') {
                l[i][j] = 1;
                if (j > 0) {
                    l[i][j] += l[i][j - 1];
                }
            }
        }
        for (int j = m - 1; j >= 0; j--) {
            if (s[i][j] == 'z') {
                r[i][j] = 1;
                if (j < m - 1) {
                    r[i][j] += r[i][j + 1];
                }
            }
        }
    }

    std::vector<std::vector<PII>> add(m);

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (r[i][j] > 0) {
                add[std::min(j + r[i][j] - 1, m - 1)].emplace_back(i, j);
            }
        }
    }

    ll ans = 0;
    std::vector fen(n + m, Fenwick<int>(m));
    for (int j = m - 1; j >= 0; j--) {
        for (auto [x, y] : add[j]) {
            fen[x + y].add(y + 1, 1);
        }
        for (int i = 0; i < n; i++) {
            auto o = std::min(ld[i][j], l[i][j]);
            if (o > 0) {
                ans += fen[i + j].sum(j - o + 2, j + 1);
            }
        }
    }

    std::cout << ans << "\n";

剩下待补(还不会flow)


电波交流