CCPC 2021 山东省赛

G. Grade Point Average

题目大意

求n个数字的平均数,保留k位输出

解题思路

C++按除法过程模拟或者Python decimal,注意要格式化转为非科学计数法输出(某些神秘原因会导致Python3 ac但是pypy3 re)

代码实现

#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, k, sum = 0, f = 1;
    std::cin >> n >> k;

    for (int i = 0; i < n; i++) {
        int x;
        std::cin >> x;
        sum += x;
    }

    while (1) {
        if (k == 0) {
            break;
        }
        k -= !f;
        std::cout << sum / n;
        sum -= sum / n * n;
        if (sum / n == 0 && f) {
            std::cout << ".";
            f = 0;
        }
        if (sum == 0) {
            break;
        }
        if (sum < n) {
            sum *= 10;
        }
    }
    std::cout << std::string(k, '0');
}
from decimal import *
getcontext().prec = int(1e5 + 10)
n, k = map(Decimal, input().split())
a = sum(map(int, input().split()))
ans = (Decimal(a) / n).quantize(Decimal("0." + ("0" * int(k - 1)) + "1"), rounding=ROUND_DOWN)
print(f"{ans:.{int(k)}f}")

H. Adventurer's Guild

题目大意

初始有H的生命和S的耐力,有n个任务,第i个任务会消耗\(h_i\)的生命和\(s_i\)的耐力,得到\(w_i\)个金币,当耐力不足的时候会扣除等价的生命,不死的情况下最多能得到多少金币

解题思路

二维背包即可,优先考虑消耗耐力

代码实现

#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, h, s;
    std::cin >> n >> h >> s;

    std::vector<i64> H(n + 1), S(n + 1), W(n + 1);
    std::vector<std::vector<i64>> dp(h + 1, std::vector<i64>(s + 1));
    for (int i = 1; i <= n; i++) {
        std::cin >> H[i] >> S[i] >> W[i];
    }

    for (int i = 1; i <= n; i++) {
        for (int j = s; j >= 0; j--) {
            for (int k = h; k >= 1; k--) {
                if (j >= S[i] && k > H[i]) {
                    dp[k][j] = std::max(dp[k][j], dp[k - H[i]][j - S[i]] + W[i]);
                } else if (j < S[i] && j + k - S[i] > H[i]) {
                    dp[k][j] = std::max(dp[k][j], dp[j + k - S[i] - H[i]][0] + W[i]);
                }
            }
        }
    }

    std::cout << dp[h][s] << "\n";
}

D. Dyson Box

题目大意

给你一个n*n的矩阵,会填充一些方格,问这些方格朝x轴和y轴掉落的周长分别是多少

解题思路

看当前添加的这个放个的xy以及周围的xy有没有其他方格就能知道每个方格的贡献

代码实现

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;

const int N = 2e5 + 10;

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

    int n, ansa = 0, ansb = 0;
    std::cin >> n;

    std::vector<int> a(N), b(N);

    for (int i = 0; i < n; i++) {
        int x, y, cnta = 4, cntb = 4;
        std::cin >> x >> y;
        if (a[x]) {
            cnta -= 2;
        }
        if (b[y]) {
            cntb -= 2;
        }
        a[x]++;
        b[y]++;
        cnta -= ((int)(a[x] <= a[x - 1]) + (int)(a[x] <= a[x + 1])) * 2;
        cntb -= ((int)(b[y] <= b[y - 1]) + (int)(b[y] <= b[y + 1])) * 2;
        ansa += cnta;
        ansb += cntb;

        std::cout << ansa << " " << ansb << "\n";
    }
}

M. Matrix Problem

题目大意

给出一个01矩阵c,要求构造两个01矩阵ab,使得\(a_{ij} \& b_{ij} = c_{ij}\),同时在这个条件下要满足ab矩阵所有的1都联通

解题思路

a第一列填1,然后隔行全填1(最后列不填),b与a的01状态完全相反,此时所有的与都是0,并且在仍和位置填1都会是联通的,最后根据\(c_{ij}\)来改变ab的值即可

代码实现

#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, m;
    std::cin >> n >> m;

    std::vector<std::vector<int>> a(n, std::vector<int>(m)), b(n, std::vector<int>(m)), c(n, std::vector<int>(m));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            char ch;
            std::cin >> ch;
            c[i][j] = ch - '0';
        }
    }

    for (int i = 0; i < n; i += 2) {
        for (int j = 0; j < m - 1; j++) {
            a[i][j] = 1;
        }
        if (i + 1 < n) {
            a[i + 1][0] = 1;
        }
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            b[i][j] = 1 - a[i][j];
        }
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (c[i][j]) {
                a[i][j] = 1;
                b[i][j] = 1;
            }
        }
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            std::cout << a[i][j];
        }
        std::cout << "\n";
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            std::cout << b[i][j];
        }
        std::cout << "\n";
    }
}

C. Cat Virus

题目大意

给树黑白染色,要求黑节点的子节点都是黑色的,请你构造出一棵树使得染色方案数恰好是k,保证有解

解题思路

设f(u)为到节点u的染色方案数

  • 当u是白色时,u的子节点v染色方案数为f(v),它们各自独立,会贡献\(\prod_{i=1}^{n} f(v_i)\)
  • 当u是黑色时候,子节点v们只能贡献1

于是得到转移方程:
\(f(u) = 1 + \prod_{i=1}^{n} (f(v_i) + 1)\)

然后dfs每次k先减去1,表示根节点已染色,在剩下的方案数中分解k即可,以构造二叉树为例,把子树方案分解为a和b使得a*b=k,对k的奇偶性分别讨论即可,log次就能算完

代码实现

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;

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

    i64 k, v = 1;
    std::cin >> k;

    std::vector<std::array<i64, 2>> g;

    auto dfs = [&](auto &&self, i64 k, int u) -> void {
        k--;
        i64 a, b;
        if (k % 2 == 1) {
            a = 1;
            b = k;
        } else {
            a = 2;
            b = k / 2;
        }
        if (a >= 2) {
            g.push_back({u, v});
            self(self, a, v++);
        }
        if (b >= 2) {
            g.push_back({u, v});
            self(self, b, v++);
        }
    };

    dfs(dfs, k, v++);
    std::cout << v - 1 << "\n";
    for (auto [u, v] : g) {
        std::cout << u << " " << v << "\n";
    }
}

B. Build Roads

题目大意

n个数字求前n-1小的两两gcd之和

解题思路

如果n较小,以gcd为边权求mst即可。如果n较大R-L较小,则n个数会把这个区间填满,相邻数字必定互质,答案是n-1;如果n较大R-L较大,则极大概率(2e5范围素数最远距离不到100)会有素数,答案还是n-1,特判LR相等的情况

还有一种启发式做法,对于每个数字都随机选点取最小的gcd作为贡献,由于上述性质,随机出错的概率较小,只要设置合适的随机次数最后也能得到答案,注意最后要减去一个最小的gcd,因为它会在产生这个gcd的两个数都计算一次

代码实现

// 做法1
#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;

struct DSU {
    std::vector<int> fa, rank, siz;
    int cnt;

    DSU(int n) : fa(n + 1), rank(n + 1), siz(n + 1, 1), cnt(n) {
        for (int i = 1; i <= n; i++) {
            fa[i] = i;
        }
    }

    int find(int x) {
        if (fa[x] != x) {
            fa[x] = find(fa[x]);
        }
        return fa[x];
    }

    void merge(int x, int y) {
        int X = find(x), Y = find(y);
        if ((X != Y)) {
            siz[X] += siz[Y];
            if (rank[X] >= rank[Y]) {
                fa[Y] = X;
                rank[X] += (int)(rank[X] == rank[Y]);
            } else {
                fa[X] = Y;
            }
            cnt--;
        }
    }

    int size() {
        return cnt;
    }

    int count(int x) {
        return siz[find(x)];
    }
};

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

    u64 n, L, R, seed, ans = 0;
    std::cin >> n >> L >> R >> seed;

    auto xorshift64 = [&]() -> u64 {
        u64 x = seed;
        x ^= x << 13;
        x ^= x >> 7;
        x ^= x << 17;
        return seed = x;
    };

    auto gen = [&]() -> int { return xorshift64() % (R - L + 1) + L; };

    std::vector<i64> a(n);
    for (int i = 0; i < n; i++) {
        a[i] = gen();
    }

    if (L == R) {
        std::cout << a[0] * (n - 1) << "\n";
    } else if (n > 1000) {
        std::cout << n - 1 << "\n";
    } else {
        DSU dsu(n);
        std::vector<std::array<i64, 3>> g;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                g.push_back({std::__gcd(a[i], a[j]), i, j});
            }
        }
        std::sort(g.begin(), g.end());

        for (auto [w, u, v] : g) {
            if (dsu.find(u) != dsu.find(v)) {
                ans += w;
                dsu.merge(u, v);
            }
        }

        std::cout << ans << "\n";
    }
}
// 做法2
#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;

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

    u64 n, L, R, seed;
    std::cin >> n >> L >> R >> seed;

    auto xorshift64 = [&]() -> u64 {
        u64 x = seed;
        x ^= x << 13;
        x ^= x >> 7;
        x ^= x << 17;
        return seed = x;
    };

    auto gen = [&]() -> int { return xorshift64() % (R - L + 1) + L; };

    std::vector<i64> a(n);
    for (int i = 0; i < n; i++) {
        a[i] = gen();
    }

    i64 ans = 0, min = LONG_LONG_MAX;
    for (int i = 0; i < n; i++) {
        i64 g = a[i];
        for (int j = 0; j < 300 && g != 1; j++) {
            g = std::min(g, std::__gcd(a[i], a[rand() % n]));
        }
        min = std::min(min, g);
        ans += g;
    }
    std::cout << ans - min << "\n";
}

F. Birthday Cake

题目大意

给你n个字符串,问有多少对字符满足加起来之后左右两部分相等

解题思路

字符串哈希,统计整个串出现的次数和去掉首尾相同部分出现的次数即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
const int MOD1 = 1e9 + 7, MOD2 = 1e9 + 9;

class StringHash {
   public:
    int P1, P2;
    std::vector<u64> h1, h2, p1, p2;

    StringHash() {
        std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());
        // P1 = std::uniform_int_distribution<int>(128, 10000)(rng);
        // P2 = std::uniform_int_distribution<int>(128, 10000)(rng);
        P1 = 131;
        P2 = 13331;
    }

    template <typename Sequence>
    void build(const Sequence& seq) {
        int n = seq.size();
        h1.resize(n + 1, 0);
        h2.resize(n + 1, 0);
        p1.resize(n + 1, 1);
        p2.resize(n + 1, 1);
        for (int i = 1; i <= n; i++) {
            h1[i] = (h1[i - 1] * P1 + seq[i - 1]) % MOD1;
            h2[i] = (h2[i - 1] * P2 + seq[i - 1]) % MOD2;
            p1[i] = (p1[i - 1] * P1) % MOD1;
            p2[i] = (p2[i - 1] * P2) % MOD2;
        }
    }

    std::pair<u64, u64> get(int l, int r) {
        u64 hash1 = (h1[r] - h1[l - 1] * p1[r - l + 1] % MOD1 + MOD1) % MOD1;
        u64 hash2 = (h2[r] - h2[l - 1] * p2[r - l + 1] % MOD2 + MOD2) % MOD2;
        return {hash1, hash2};
    }
};

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

    StringHash sh;
    std::map<std::pair<u64, u64>, int> cnt1, cnt2;

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

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

        sh.build(s);
        int siz = s.size();
        auto tmp = sh.get(1, siz);
        ans += cnt1[tmp]++;
        ans += cnt2[tmp];

        for (int len = 1; len * 2 <= siz; len++) {
            auto pre = sh.get(1, len);
            auto suff = sh.get(siz - len + 1, siz);
            if (pre == suff) {
                auto mid = sh.get(len + 1, siz - len);
                ans += cnt1[mid];
                cnt2[mid]++;
            }
        }
    }

    std::cout << ans << "\n";
}
posted @ 2025-05-08 19:38  udiandianis  阅读(53)  评论(0)    收藏  举报