CCPC 2021 吉林省赛

M. Sequence

题目大意

给定一个序列,求序列长度和极差的乘积

解题思路

按题意直接计算即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    i64 n, max = LONG_LONG_MIN, min = LONG_LONG_MAX;
    std::cin >> n;

    for (int i = 0; i < n; i++) {
        i64 x;
        std::cin >> x;
        max = std::max(max, x);
        min = std::min(min, x);
    }

    std::cout << n * (max - min) << "\n";
}

A. Random Number Checker

题目大意

给定一个数组,问奇偶出现次数是不是只相差不超过一次

解题思路

按题意直接计算即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int n, cnt = 0;
    std::cin >> n;

    for (int i = 0; i < n; i++) {
        int x;
        std::cin >> x;
        if (x % 2) {
            cnt--;
        } else {
            cnt++;
        }
    }

    if (std::abs(cnt) <= 1) {
        std::cout << "Good\n";
    } else {
        std::cout << "Not Good\n";
    }
}

B. Arithmetic Exercise

题目大意

输出a/b保留四舍五入保留k位

解题思路

C++模拟除法过程或者Python直接decimal

代码实现

from decimal import *
getcontext().prec = 1001
a, b, c = map(Decimal, input().split())
print((a / b).quantize(Decimal("0." + ("0" * int(c - 1)) + "1"), rounding = ROUND_HALF_UP))

E. Great Detective TJC

题目大意

给定一个数组,问是否存在两个两个数异或值是1

解题思路

先对数组去重,然后看是不是会有/2之后相等的数字,/2相等说明之前都一样,因为去过重所以只有最后一位不一样,也就是异或为1

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n, f = 0;
        std::cin >> n;

        std::set<int> st;
        for (int i = 0; i < n; i++) {
            int x;
            std::cin >> x;
            st.insert(x);
        }

        std::map<int, int> mp;
        for (auto x : st) {
            mp[x >> 1]++;
            if (mp[x >> 1] > 1) {
                f = 1;
            }
        }

        if (f) {
            std::cout << "Yes\n";
        } else {
            std::cout << "No\n";
        }
    }
}

L. Suzuran Loves String

题目大意

给定一个字符串,每次操作可以在字符串末尾添加或删除一个字符,现在要求选择字符串的两个后缀s和t,最大化让s变成t的最小操作次数

解题思路

只需要找到字符串第一个和开头不一样的位置开始就是最长的,因为此时要把他们全部删完才能相等,特判全都一样的情况

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        std::string s;
        std::cin >> s;

        int n = s.size(), ans = 0;
        for (int i = 1; i < n; i++) {
            if (s[i] != s[i - 1]) {
                ans = n + n - i;
                break;
            }
        }

        if (!ans) {
            ans = n - 1;
        }

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

K. Bracket Sequence

题目大意

求k种n对括号的匹配数,对答案模1e9+7

解题思路

括号匹配数就是卡特兰数,每个位置的括号有k种,因此答案就是\(k^n*catalan(n)\%MOD\)

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const int N = 1e6 + 10;
const int MOD = 1e9 + 7;

std::vector<i64> fac(N + 1, 1), invfac(N + 1, 1);

i64 ksm(i64 a, i64 n, i64 mod) {
    i64 res = 1;
    a = (a % mod + mod) % mod;
    while (n) {
        if (n & 1) {
            res = (a * res) % mod;
        }
        a = (a * a) % mod;
        n >>= 1;
    }
    return res;
}

void init(int n) {
    fac[0] = 1;
    for (int i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i % MOD;
    }
    invfac[n] = ksm(fac[n], MOD - 2, MOD);
    for (int i = n - 1; i >= 0; i--) {
        invfac[i] = invfac[i + 1] * (i + 1) % MOD;
    }
}

i64 C(int n, int m) {  // 组合数
    if (m > n || m < 0) {
        return 0;
    }
    return fac[n] * invfac[m] % MOD * invfac[n - m] % MOD;
}

i64 A(int n, int m) {  // 排列数
    if (m > n || m < 0) {
        return 0;
    }
    return fac[n] * invfac[n - m] % MOD;
}

// n 对括号的合法匹配数,有 n 个节点的二叉树的种类数
// 从对角线下方走到对角线的路径数,栈的出栈序列数
i64 catalan(int n) {  // 卡特兰数
    if (n < 0) {
        return 0;
    }
    return C(2 * n, n) * ksm(n + 1, MOD - 2, MOD) % MOD;
}

// 将 n 个不同的元素划分到 k 个非空集合中的方案数
i64 stirling2(int n, int k) {  // 第二类斯特林数
    if (k > n || k < 0) {
        return 0;
    }
    i64 res = 0;
    for (int i = 0; i <= k; i++) {
        i64 term = C(k, i) * ksm(k - i, n, MOD) % MOD;
        if (i % 2 == 1) {
            term = (MOD - term) % MOD;
        }
        res = (res + term) % MOD;
    }
    return res * invfac[k] % MOD;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    init(N);
    int n, k;
    std::cin >> n >> k;

    std::cout << ksm(k, n, MOD) * catalan(n) % MOD << "\n";
}

H. Visit the Park

题目大意

给定一张n个点m条带权边(0~9)的无向图,给定规划路线,uv联通时等价选择任意一条路,当通过这条路的时候,他会从左到右写下这个数字,求写个数字的期望,以模998244353的逆元形式表示

解题思路

只需要统计两节点间路径数和边权得到这一次移动的期望,然后依次算完整个路线即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const int MOD = 998244853;

i64 ksm(i64 a, i64 n, i64 mod) {
    i64 res = 1;
    a = (a % mod + mod) % mod;
    while (n) {
        if (n & 1) {
            res = (a * res) % mod;
        }
        a = (a * a) % mod;
        n >>= 1;
    }
    return res;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    i64 n, m, k, ans = 0;
    std::cin >> n >> m >> k;

    std::vector<std::vector<std::array<int, 2>>> g(n + 1, std::vector<std::array<int, 2>>());
    for (int i = 0; i < m; i++) {
        int u, v, w;
        std::cin >> u >> v >> w;
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }

    std::vector<int> a(k);
    for (int i = 0; i < k; i++) {
        std::cin >> a[i];
    }

    for (int i = 0; i < k - 1; i++) {
        i64 siz = 0, pre = 0;
        std::map<int, int> mp;
        for (auto [u, w] : g[a[i]]) {
            if (u == a[i + 1]) {
                mp[w]++;
                siz++;
            }
        }
        if (siz == 0) {
            std::cout << "Stupid Msacywy!\n";
            return 0;
        }
        for (auto [a, b] : mp) {
            pre = (pre + (ans * 10 + a) % MOD * b % MOD * ksm(siz, MOD - 2, MOD) % MOD) % MOD;
        }
        ans = pre % MOD;
    }

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

G. Matrix Repair

题目大意

给定一个n*n的01矩阵,有一些位置的元素值是未知的,然后再给你所有的行列异或和,要求你还原出这个01矩阵,不可能则输出-1

解题思路

首先根据已知元素计算当前所有行列的异或和,然后根据最终的行列异或和反推填充只有一个元素是未知的行列即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);

    int n, cnt = 0, f = 1;
    std::cin >> n;

    std::vector<std::vector<int>> g(n, std::vector<int>(n));
    std::vector<int> xorrow(n), xorcol(n), row(n), col(n);
    std::vector<std::set<std::array<int, 2>>> posrow(n), poscol(n);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            std::cin >> g[i][j];
            if (g[i][j] != -1) {
                xorrow[i] ^= g[i][j];
                xorcol[j] ^= g[i][j];
            } else {
                posrow[i].insert({i, j});
                poscol[j].insert({i, j});
                cnt++;
            }
        }
    }
    for (int i = 0; i < n; i++) {
        std::cin >> row[i];
    }
    for (int i = 0; i < n; i++) {
        std::cin >> col[i];
    }

    while (f) {
        f = 0;
        for (int i = 0; i < n; i++) {
            if (posrow[i].size() == 1) {
                auto [ii, jj] = *posrow[i].begin();
                g[ii][jj] = xorrow[ii] ^ row[ii];
                xorrow[ii] ^= g[ii][jj];
                xorcol[jj] ^= g[ii][jj];
                posrow[ii].erase({ii, jj});
                poscol[jj].erase({ii, jj});
                cnt--;
                f = 1;
            }
            if (poscol[i].size() == 1) {
                auto [ii, jj] = *poscol[i].begin();
                g[ii][jj] = xorcol[jj] ^ col[jj];
                xorrow[ii] ^= g[ii][jj];
                xorcol[jj] ^= g[ii][jj];
                posrow[ii].erase({ii, jj});
                poscol[jj].erase({ii, jj});
                cnt--;
                f = 1;
            }
        }
    }

    if (cnt) {
        std::cout << -1 << "\n";
    } else {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                std::cout << g[i][j] << " \n"[j == n - 1];
            }
        }
    }
}

I. Nim Game

题目大意

给定一个数组,可以有两种操作

  • “1 l r x”:给lr区间都加上x
  • “2 l r”:在lr区间进行nim博弈问后手是否必胜

解题思路

区间修改可以用树状数组或线段树来实现,但是区间异或和缺无法快速查询,但是nim博弈的结论是看异或和是否非零,因此可以考虑用线性基来维护这一段区间,\(a_i\)只有1e9,长度大于31的区间显然不能把所有数字都插入线性基,否则通过能插入线性基判断区间异或和是否为0

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const i64 M = 1e5 + 10;

i64 tree1[M], tree2[M];

i64 lowbit(i64 x) {
    return x & (-x);
}

void add(i64 tree[], i64 pos, i64 x) {
    for (i64 i = pos; i < M; i += lowbit(i)) {
        tree[i] += x;
    }
}

void range_add(i64 l, i64 r, i64 x) {
    add(tree1, l, x);
    add(tree1, r + 1, -x);
    add(tree2, l, x * (l - 1));
    add(tree2, r + 1, -x * r);
}

i64 ask(i64 tree[], i64 pos) {
    i64 res = 0;
    for (i64 i = pos; i; i -= lowbit(i)) {
        res += tree[i];
    }
    return res;
}

i64 query(i64 l, i64 r) {
    i64 resr = ask(tree1, r) * r - ask(tree2, r);
    i64 resl = ask(tree1, l - 1) * (l - 1) - ask(tree2, l - 1);
    return resr - resl;
}

int base[35];

bool insert(int x) {
    for (int i = 31; i >= 0; i--) {
        if (!(x >> i)) {
            continue;
        }
        if (!base[i]) {
            base[i] = x;
            return 1;
        }
        x ^= base[i];
    }
    return 0;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

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

    for (i64 i = 1; i <= n; i++) {
        i64 x;
        std::cin >> x;
        range_add(i, i, x);
    }

    while (m--) {
        i64 op;
        std::cin >> op;
        if (op == 1) {
            i64 l, r, x;
            std::cin >> l >> r >> x;
            range_add(l, r, x);
        } else {
            int l, r, f = 0;
            std::cin >> l >> r;
            if (r - l > 31) {
                std::cout << "Yes\n";
                continue;
            }
            std::memset(base, 0, sizeof(base));
            for (int i = l; i <= r; i++) {
                if (!insert(query(i, i))) {
                    f = 1;
                }
            }

            if (f) {
                std::cout << "Yes\n";
            } else {
                std::cout << "No\n";
            }
        }
    }
}

C. Random Number Generator

题目大意

给定递推式\(X_{n+1} = (aX_n + b) \mod m\)和a b m \(X_0\)\(x\)是否能由上面的参数递推出来

解题思路

这个递推式形式上像离散对数,尝试将它变形看能否变为离散对数的形式。

\(X_{n+1} = (aX_n + b) \mod m\)

\(X_1 = (aX_0 + b) \mod m\)
\(X_2 = (a^2X_0 + ab + b) \mod m = (a^2X_0 + b(1 + a)) \mod m\)
\(X_3 = (a^3X_0 + a^2b + ab + b) \mod m = (a^3X_0 + b(1 + a + a^2)) \mod m\)

\(X_n = (a^nX_0 + b(1+a+a^2+...+a^n)) \mod m\)
\(=(a^nX_0 + b\frac{1-a^n}{1-a}) \mod m\)
\(=(a^n(X_0 + \frac{b}{a-1})-\frac{b}{a-1}) \mod m\)

转化为求\(a^n \equiv \frac{X_n + \frac{b}{a-1}}{X_0 + \frac{b}{a-1}} \mod m\),直接BSGS即可求解,特判a=1且b=0的情况

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const i64 INF = 4e18;

i64 ksm(i64 a, i64 n, i64 mod) {
    i64 res = 1;
    a = (a % mod + mod) % mod;
    while (n) {
        if (n & 1) {
            res = (a * res) % mod;
        }
        a = (a * a) % mod;
        n >>= 1;
    }
    return res;
}

i64 BSGS(i64 a, i64 b, i64 m, i64 k = 1) {
    std::unordered_map<i64, i64> hash;
    hash.clear();
    i64 cur = 1, t = sqrt(m) + 1;
    for (int B = 1; B <= t; B++) {
        (cur *= a) %= m;
        hash[b * cur % m] = B;
    }
    i64 now = cur * k % m;
    for (int A = 1; A <= t; A++) {
        auto it = hash.find(now);
        if (it != hash.end()) {
            return A * t - it->second;
        }
        (now *= cur) %= m;
    }
    return -INF;
}

i64 exBSGS(i64 a, i64 b, i64 m, i64 k = 1) {
    i64 A = a %= m, B = b %= m, M = m;
    if (b == 1) {
        return 0;
    }
    i64 cur = 1 % m;
    for (int i = 0;; i++) {
        if (cur == B) {
            return i;
        }
        cur = cur * A % M;
        i64 d = std::__gcd(a, m);
        if (b % d) {
            return -INF;
        }
        if (d == 1) {
            return BSGS(a, b, m, k * a % m) + i + 1;
        }
        k = k * a / d % m, b /= d, m /= d;
    }
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    i64 a, b, m, X0, x;
    std::cin >> a >> b >> m >> X0 >> x;

    if (x == X0) {
        std::cout << "YES\n";
    } else if (a == 0) {
        if (x == b % m) {
            std::cout << "YES\n";
        } else {
            std::cout << "NO\n";
        }
    } else if (a == 1 && b == 0) {
        std::cout << "NO\n";
    } else {
        i64 aa = a, bb = (x * (a - 1) + b % m) % m * ksm((X0 * (a - 1) + b) % m, m - 2, m) % m;
        if (exBSGS(aa, bb, m) < 0) {
            std::cout << "NO\n";
        } else {
            std::cout << "YES\n";
        }
    }
}
posted @ 2025-05-08 16:26  udiandianis  阅读(21)  评论(0)    收藏  举报