HBCPC 2023 湖北省赛

M. Different Billing

题目大意

有三种队伍参加比赛,A类免费,B类缴费1000元,C类缴费2500元,已知x个队伍参赛,收费y元,求任意满足的一组ABC

解题思路

暴力枚举c倒推ba判断合法性即可

代码实现

#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 x, y, a, b = 0, c = -1;
    std::cin >> x >> y;

    for (int i = 0; i <= 1e9 / 2500; i++) {
        c++;
        b = (y - 2500 * c) / 1000;
        a = x - b - c;
        if (1000 * b + 2500 * c == y && a >= 0 && b >= 0 && c >= 0) {
            std::cout << a << " " << b << " " << c << "\n";
            return 0;
        }
    }
    std::cout << -1 << "\n";
}

C. Darkness I

题目大意

有一个n*m的黑白矩阵,如果一个白格和两个黑格相邻,则它会被染黑,问矩阵初始至少要有多少个黑格才能让整个矩阵被染黑

解题思路

选一侧长和宽间隔着染黑即可

代码实现

#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::cout << (m + n + 1) / 2 << "\n";
}

K. Dice Game

题目大意

n+1个人玩1个m面的骰子,如果最小值有多个则会重投骰子,问当固定第一个人投出1~m的点数时输的概率

解题思路

当投出和第一个人相同点数的时候始终都会重投,因此有效的点数为m-1,获胜的点数为m-i,最终的概率为\((\frac{m-i}{m-1})^n\)

代码实现

#include <bits/stdc++.h>

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

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);

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

    for (int i = 1; i <= m; i++) {
        std::cout << ksm(m - i, n, MOD) * ksm(ksm(m - 1, n, MOD), MOD - 2, MOD) % MOD << " \n"[i == m];
    }
}

H. Binary Craziness

题目大意

给定一张图,定义函数 \(f(u, v) = (deg_u \bigoplus deg_v)(deg_u | deg_v)(deg_u \& deg_v)\)\(deg_u\)表示\(u\)的度,求\(\sum_{i=1}^{n} \sum_{j=i}^{n} f(i,j)\%998244353\)

解题思路

显然图中的节点会有大量重复的度,因此统计度的大小和数量,暴力枚举即可,注意数据范围可能爆longlong和重边

代码实现

#include <bits/stdc++.h>

using i64 = long long;
using i128 = __int128;
const int MOD = 998244353;

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;
}

std::ostream &operator<<(std::ostream &out, i128 val) {
    if (val == 0) {
        return out << "0";
    }
    if (val < 0) {
        out << '-', val = -val;
    }
    std::string s;
    while (val > 0) {
        s += '0' + val % 10;
        val /= 10;
    }
    std::reverse(s.begin(), s.end());
    return out << s;
}

std::istream &operator>>(std::istream &in, i128 &val) {
    std::string s;
    in >> s;
    val = 0;
    bool neg = (s[0] == '-');
    for (int i = neg; i < s.size(); i++) {
        val = val * 10 + (s[i] - '0');
    }
    if (neg) {
        val = -val;
    }
    return in;
}

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

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

    std::vector<i128> in(n + 1);
    std::set<std::array<i128, 2>> edge;
    for (int i = 0; i < m; i++) {
        i128 u, v;
        std::cin >> u >> v;
        if (u > v) {
            std::swap(u, v);
        }
        edge.insert({u, v});
    }

    for (auto [u, v] : edge) {
        in[u]++;
        in[v]++;
    }

    std::map<i128, i128> mp;
    for (auto x : in) {
        if (x) {
            mp[x]++;
        }
    }

    i128 ans = 0;
    for (auto [x1, y1] : mp) {
        for (auto [x2, y2] : mp) {
            ans += y2 * y1 % MOD * ((x1 ^ x2) * (x1 | x2) % MOD * (x1 & x2) % MOD);
            ans %= MOD;
        }
    }

    std::cout << ans * ksm(2, MOD - 2, MOD) % MOD << "\n";
}

J. Expansion

题目大意

初始你在一个资源数组的0位置并且有0的资源,每秒你可以选择向后移动一位或者停留在原地,然后得到前缀和的资源,如果走到了末尾则会一直停留,要求无限时间下资源都不能小于0,问在这种情况下第几秒走到最末尾的位置,如果不可能则输出-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 n;
    std::cin >> n;

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

    if (pre[1] < 0 || pre[n] < 0) {
        std::cout << -1 << "\n";
        return 0;
    }

    i64 ans = 0, now = 0, max = 0;
    for (int i = 1; i <= n; i++) {
        now += pre[i];
        max = std::max(max, pre[i]);
        ans++;
        if (now < 0) {
            if (max <= 0) {
                ans = -1;
                break;
            }
            ans += (-now + max - 1) / max;
            now += (-now + max - 1) / max * max;
        }
    }

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

F. Inverse Manacher

题目大意

构造一个ab字符串,要求满足Manacher算法下的中心回文长度数组,保证有解

解题思路

分割符的中心回文长度是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;
    std::cin >> n;

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

    std::string ans = "a";
    for (int i = 3, f = 0; i < 2 * n; i += 2) {
        if (a[i] == 1) {
            ans += "ab"[f ^= 1];
        } else {
            ans += "ab"[f];
        }
    }

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

I. Step

题目大意

给定一个数组a,找到最小的t使得t(t+1)是2*lcm(a)的倍数

解题思路

对2lcm质因数分解,质数的数量一定不会太多(最小的16个质数相乘都超过1e18了),dfs暴力枚举每一个质因数的幂是属于t(设为a)还是t+1(设为b),而a与b,t与t+1都是互质的,因此可以联系到求解方程ax+by=gcd(a,b)=1的最小ax,exgcd枚举途中取最小即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;
using i128 = __int128;

std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());
namespace Factorizer {
std::vector<int> primes, least;
void sieve(int n) {
    std::vector<int> nums;
    least.assign(n + 1, 0);
    for (int i = 2; i <= n; i++) {
        if (least[i] == 0) {
            least[i] = i;
            nums.push_back(i);
        }
        for (auto p : nums) {
            if (i * p > n) {
                break;
            }
            least[i * p] = p;
            if (p == least[i]) {
                break;
            }
        }
    }
    primes = nums;
}
bool miller_rabin(i64 n) {
    if (n <= 1 || (n != 2 && n % 2 == 0)) {
        return false;
    }
    for (auto a : {3, 5, 7, 11, 13, 17, 19, 23, 29}) {
        if (n % a == 0) {
            return n == a;
        }
    }
    if (n < 31 * 31) {
        return true;
    }
    i64 d = n - 1;
    while (d % 2 == 0) {
        d /= 2;
    }
    for (i64 a : {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}) {
        if (n == a) {
            return true;
        }
        i64 t = d, y = 1 % n;
        for (i64 _t = t; _t != 0; _t >>= 1) {
            if (_t & 1) {
                y = (i128)y * a % n;
            }
            a = (i128)a * a % n;
        }
        while (t != n - 1 && y != 1 && y != n - 1) {
            y = (i128)y * y % n;
            t <<= 1;
        }
        if (y != n - 1 && t % 2 == 0) {
            return false;
        }
    }
    return true;
}
i64 pollard_rho(i64 n) {
    if (n == 1 || miller_rabin(n)) {
        return n;
    }
    i64 now = 0;
    do {
        i64 t = std::gcd(++now, n), r = t, g = 1;
        if (t != 1 && t != n) {
            return t;
        }
        do {
            t = ((i128)t * t % n + now) % n;
            r = ((i128)r * r % n + now) % n;
            r = ((i128)r * r % n + now) % n;
        } while ((g = std::gcd(abs(t - r), n)) == 1);
        if (g != n) {
            return g;
        }
    } while (now < n / now);
    return 0;
}
std::vector<i64> factor(i64 n) {
    if (n == 1) {
        return {};
    }
    std::vector<i64> g, d;
    d.push_back(n);
    while (!d.empty()) {
        auto v = d.back();
        d.pop_back();
        auto rho = pollard_rho(v);
        if (rho == v) {
            g.push_back(rho);
        } else {
            d.push_back(rho);
            d.push_back(v / rho);
        }
    }
    std::sort(g.begin(), g.end());
    return g;
}
}  // namespace Factorizer

void exgcd(i64 a, i64 b, i64 &x, i64 &y) {
    if (b == 0) {
        x = 1, y = 0;
        return;
    }
    exgcd(b, a % b, y, x);
    y -= a / b * x;
}

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

    i64 n, lcm = 1;
    std::cin >> n;

    std::vector<int> a(n);
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
        lcm = std::lcm(lcm, a[i] * 2);
    }

    std::vector<i64> fac = Factorizer::factor(lcm), f;
    std::unordered_map<i64, i64> ump;
    for (auto x : fac) {
        if (ump.count(x)) {
            ump[x] *= x;
        } else {
            ump[x] = x;
        }
    }

    for (auto [x, y] : ump) {
        f.push_back(y);
    }

    i64 siz = f.size(), ans = 4e18;
    auto dfs = [&](auto &&self, int pos, i64 a) {
        if (pos == siz) {
            i64 x = 0, y = 0, b = lcm / a;
            exgcd(a, b, x, y);
            x = ((-x) % b + b) % b;
            if (x == 0) {
                x = b;
            }
            ans = std::min(ans, a * x);
            return;
        }
        self(self, pos + 1, a);
        self(self, pos + 1, a * f[pos]);
    };

    dfs(dfs, 0, 1);

    std::cout << ans << "\n";
}
posted @ 2025-05-04 23:59  udiandianis  阅读(42)  评论(0)    收藏  举报