CCPC 2024 山东省赛
I. Left Shifting
题目大意
给一个字符串,每次操作可以取最左边的字符放在最右边,问最少操作多少次能让首尾字符相等,无法实现输出-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--) {
std::string s;
std::cin >> s;
int ans = -1, n = s.size();
for (int i = 1; i < n; i++) {
if (s[i] == s[i - 1]) {
ans = i;
break;
}
}
if (s.front() == s.back()) {
ans = 0;
}
std::cout << ans << "\n";
}
}
A. Printer
题目大意
n台打印机需要k份文件,每t秒可以打印一份,打印l份后需要休息w秒,问至少需要多久才能完成打印
解题思路
文件数量和时间存在单调关系,所以可以二分答案,check途中需要注意提前退出,否则会爆longlong
代码实现
#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, k;
std::cin >> n >> k;
std::vector<std::array<i64, 3>> tlw(n);
for (int i = 0; i < n; i++) {
std::cin >> tlw[i][0] >> tlw[i][1] >> tlw[i][2];
}
auto check = [&](i64 x) -> bool {
i64 sum = 0;
for (auto [t, l, w] : tlw) {
sum += x / (t * l + w) * l + std::min(x % (t * l + w) / t, l);
if (sum >= k) {
return true;
}
}
return sum >= k;
};
i64 l = 0, r = 4e18, ans = 0;
while (l <= r) {
i64 mid = (l + r) / 2;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
std::cout << ans << "\n";
}
}
K. Matrix
题目大意
给定一个n,要求构造出一个n*n的矩阵,1-2n的元素至少出现一次,要求矩阵只存在一个由两条水平线和两条垂线交点组成的四元组,四个的元素互不相等
解题思路
显然让这些水平线和垂线都由一个元素构成就能满足,一种较为简单的构造方法是,让4个不同的数字在左上角形成一个2*2的矩阵,接下来往右边填充n-2个数字列,最后填充剩下n-2个数字行,刚好用完2n个数
代码实现
#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::cout << "Yes\n";
std::vector<std::vector<int>> ans(n, std::vector<int>(n));
ans[0][0] = 1;
ans[0][1] = 2;
ans[1][0] = 3;
ans[1][1] = 4;
for (int i = 2; i < n; i++) {
ans[0][i] = 3 + i;
ans[1][i] = 3 + i;
}
int x = ans[0].back() + 1;
for (int i = 2; i < n; i++) {
for (int j = 0; j < n; j++) {
ans[i][j] = x;
}
x++;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
std::cout << ans[i][j] << " \n"[j == n - 1];
}
}
}
F. Divide the Sequence
题目大意
给你一个数组,可以把数组分成k个子串,第i份的权重为i,最后的贡献为权重乘上第i份的和,问k为1到n的最大贡献
解题思路
显然只要每次贪心地取最大的后缀和累加即可,注意只用对后n-1个后缀排序,因为k=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--) {
i64 n, ans;
std::cin >> n;
std::vector<i64> suff(n);
for (int i = 0; i < n; i++) {
std::cin >> suff[i];
}
for (int i = n - 2; i >= 0; i--) {
suff[i] += suff[i + 1];
}
std::sort(suff.begin() + 1, suff.end());
ans = suff[0];
std::cout << ans << " \n"[n == 1];
for (int i = n - 1; i >= 1; i--) {
ans += suff[i];
std::cout << ans << " \n"[i == 1];
}
}
}
C. Colorful Segments 2
题目大意
给n个lr区间,有交集的区间不能用相同的颜色,一共有k种颜色,问有多少种染色方案,对答案模998244353
解题思路
一个区间的染色数量会被影响就是看他和多少个区间有交集,可以对区间的l排序,然后维护r,每次都看当前这个区间和多少区间有交集,k减去交集的数量就是可以染的颜色数量,记得最后对答案再去一次模,要考虑只有一个区间但是k超过模数的情况
代码实现
#include <bits/stdc++.h>
using i64 = long long;
const int MOD = 998244353;
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, k;
std::cin >> n >> k;
std::vector<std::array<int, 2>> lr(n);
std::priority_queue<std::array<int, 2>, std::vector<std::array<int, 2>>, std::greater<>> pq;
for (int i = 0; i < n; i++) {
std::cin >> lr[i][0] >> lr[i][1];
}
std::sort(lr.begin(), lr.end());
i64 sum = k, cnt = 1;
for (int i = 0; i < n; i++) {
if (!pq.empty()) {
while (!pq.empty()) {
auto [r, l] = pq.top();
while (r < lr[i][0] && !pq.empty()) {
pq.pop();
r = pq.top()[0];
}
break;
}
sum = (sum * std::max(0ll, k - (i64)pq.size())) % MOD;
}
pq.push({lr[i][1], lr[i][0]});
}
std::cout << sum % MOD << "\n";
}
}
J. Colorful Spanning Tree
题目大意
给n个颜色数量,第i个表示颜色为i的节点数量。再给一个邻接矩阵,\(g[i][j]\) 表示i颜色和j颜色连边的权值,问这些节点构成的最小生成树权值和是多少
解题思路
题目的数据范围很大,硬跑最小生成树显然不可以,但是颜色数量比较少,可以从此入手。考虑所有颜色都只有一个节点的情况,发现答案就是最小生成树权值和,同时发现复杂度允许 \(n^2\) 暴力维护每一个颜色最小权值的连边颜色,因此只需要跑一遍最小生成树,剩下的同色都挂当前颜色最小颜色节点上即可
代码实现
#include <bits/stdc++.h>
using i64 = 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);
int tt;
std::cin >> tt;
while (tt--) {
i64 n, ans = 0;
std::cin >> n;
std::vector<i64> a(n + 1), minw(n + 1, LONG_LONG_MAX);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
DSU dsu(n);
std::vector<std::array<i64, 3>> g;
for (int u = 1; u <= n; u++) {
for (int v = 1; v <= n; v++) {
i64 w;
std::cin >> w;
minw[u] = std::min(minw[u], w);
if (u != v) {
g.push_back({w, u, v});
}
}
}
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);
}
}
for (int i = 1; i <= n; i++) {
ans += (a[i] - 1) * minw[i];
}
std::cout << ans << "\n";
}
}
D. Hero of the Kingdom
题目大意
买入x件商品需要ax+b秒和px金币,卖出x件商品需要cx+d秒但可以赚qx金币。有t秒以及m金币,问t秒结束后最多能有多少金币
解题思路
首先考虑到的显然是每次尽可能多的买,但是当花费时间和利润很低t很大的时候会被卡到接近O(t)的复杂度,不难发现如果m/p的值一直都是相同的,那么操作流程都是一样的,因此可以直接算出让m/p发生变化需要购买的数量,具体操作见代码和注释
代码实现
#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--) {
i64 p, a, b, q, c, d, m, t;
std::cin >> p >> a >> b >> q >> c >> d >> m >> t;
while (t > 0) {
i64 X1 = m / p; // 一次最多买多少
if (X1 == 0) {
break;
}
i64 X2 = std::max(X1, ((X1 + 1) * p - m + q - p - 1) / (q - p)); // 让m/p增加1要买多少
i64 cnt = (X2 + X1 - 1) / X1; // 要买多少次
X2 = std::max(X2, X1 * cnt);
i64 T = X2 * (a + c) + cnt * (b + d);
if (t >= T) {
t -= T;
m += X2 * (q - p);
} else {
cnt = t / (X1 * (a + c) + b + d); // 剩余时间内最多买多少次
X2 = cnt * X1;
T = X2 * (a + c) + cnt * (b + d);
t -= T + b + d;
m += X2 * (q - p);
if (t > 0) {
m += t / (a + c) * (q - p);
}
break;
}
}
std::cout << m << "\n";
}
}