ICPC 2025 南昌邀请赛
A. Nezha Naohai
题目大意
有三件事,分别要花费A,B,C 小时。每个小时会累计D条的抱怨。请问一共累计了多少条抱怨。
解题思路
输出(A+B+C)*D即可
代码实现
#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 a, b, c, d;
std::cin >> a >> b >> c >> d;
std::cout << (a + b + c) * d << "\n";
}
F. Caloric Difference
题目大意
规划接下来n天的热量差,假设第i天摄入的热量为\(c_i\),则第i天消耗的热量为\(b_i=pb_{i−1}+(1−p)c_{i−1}\),给出\(b_0,c_0\)和p。有k个位置的\(c_i\)为固定值,表示要吃放纵餐和每个\(c_i\)必须在全局给出的[L,R]之间选择。问n天内最大的(总热量消耗-总热量摄入)值。
解题思路
考虑每个\(c_i\)的贡献,化简后为\(-c_ip^{n-i}\),\(c_i\)越小,答案越大,因此没固定的\(r_i\)全取L即可,注意浮点数
代码实现
#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;
double r0, c0, p, L, R;
std::vector<double> r(n + 1), c(n + 1);
std::cin >> r[0] >> c[0] >> p >> L >> R;
for (int i = 1; i <= k; i++) {
int p;
double v;
std::cin >> p >> v;
r[p] = v;
}
double ans = 0;
for (int i = 1; i <= n; i++) {
if (r[i] == 0.0) {
r[i] = L;
}
c[i] = p * c[i - 1] + (1 - p) * r[i - 1];
ans += c[i] - r[i];
}
std::cout << std::fixed << std::setprecision(6) << ans << "\n";
}
}
D. Virtuous Pope
题目大意
给定三维空间内的若干条线段,限制其端点在一给定长方体上,求对于任意与坐标轴垂直的平面最多能和多少条线段相交。
解题思路
把每个线段分别对xyz平面做映射,就把问题转化为了求数轴最大重叠线段数量
代码实现
#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, a, b, c;
std::cin >> n >> a >> b >> c;
std::vector<std::array<int, 2>> x, y, z;
for (int i = 0; i < n; i++) {
int x1, y1, z1, x2, y2, z2;
std::cin >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
x.push_back({std::min(x1, x2), 1});
x.push_back({std::max(x1, x2), -1});
y.push_back({std::min(y1, y2), 1});
y.push_back({std::max(y1, y2), -1});
z.push_back({std::min(z1, z2), 1});
z.push_back({std::max(z1, z2), -1});
}
std::sort(x.begin(), x.end(), [&](auto a, auto b) {
if (a[0] != b[0]) {
return a[0] < b[0];
}
return a[1] > b[1];
});
std::sort(y.begin(), y.end(), [&](auto a, auto b) {
if (a[0] != b[0]) {
return a[0] < b[0];
}
return a[1] > b[1];
});
std::sort(z.begin(), z.end(), [&](auto a, auto b) {
if (a[0] != b[0]) {
return a[0] < b[0];
}
return a[1] > b[1];
});
int ans = 0, X = 0, Y = 0, Z = 0;
for (int i = 0; i < 2 * n; i++) {
X += x[i][1];
Y += y[i][1];
Z += z[i][1];
ans = std::max({ans, X, Y, Z});
}
std::cout << ans << "\n";
}
K. Rotation
题目大意
有n个雕像,雕像朝向4个方向:前方、右方、后方或左方。
每次你都可以进行以下一种操作
- 选一个雕像不动,其他所有雕像同时顺时针旋转90度
- 使所有雕像顺时针旋转90度
问要使所有雕像面向前方最少需要操作多少次
解题思路
可以发现,按某个雕像转一次再按住自己转3次就等于往反方向转了一次,先转到同向,然后枚举2操作即可
代码实现
#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<int> a(n + 1), b(4);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= 3; j++) {
b[j] += (a[i] - j + 4) % 4;
}
}
int ans = INT_MAX;
for (int i = 0; i <= 3; i++) {
ans = std::min(ans, b[i] + (b[i] * 3 + 4 - i) % 4);
}
std::cout << ans << "\n";
}
M. Divide coins
题目大意
有n枚硬币,其中k枚正面朝下,但你不知道是哪k枚。你可以翻转硬
币,问如何操作能将硬币分成两组,让两组硬币正面朝上的一样多。
解题思路
第一堆选k枚,第二堆选n−k枚,然后把第二堆全部翻转即可。
假设第一堆中有x个朝下了,则有k-x个朝上,第二堆有\(n-k-(k-x)=n-2*k+x\)个朝上,翻转第二堆则有\(n-k-(n-2*k+x)=k-x\)个朝上
代码实现
#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, k;
std::cin >> n >> k;
std::cout << std::string(k, '1') + std::string(n - k, '4') << "\n";
}
G. Exploration
题目大意
有向有权图,每次给起始点p,起始体力x,通过一条边权为d边后,体力变为\(\lfloor \frac{p}{x} \rfloor\),多次询问,求每次将体力消耗完要经过的最小步数。
解题思路
因为\(d \geq 2\)且\(x \leq 10^9\),所以这条路不会特别长,考虑设计状态dp[i][j]为在j出发i步之后的最小乘积,暴力更新所有可行边即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int n, m, q;
std::cin >> n >> m >> q;
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, d;
std::cin >> u >> v >> d;
g[u].push_back({v, d});
}
std::vector<std::vector<i64>> dp(31, std::vector<i64>(n + 1, 1));
for (int i = 1; i <= 30; i++) {
for (int j = 1; j <= n; j++) {
for (auto [u, d] : g[j]) {
dp[i][j] = std::max(dp[i][j], dp[i - 1][u] * d);
}
}
}
while (q--) {
int p, x;
std::cin >> p >> x;
for (int i = 1; i <= 30; i++) {
if (dp[i][p] > x) {
std::cout << i << "\n";
break;
}
}
}
}
I. Dating Day
题目大意
给定一个长度为n的01串,可以对一个1的数量为k的区间进行一次任意排序,问能得到多少种不同的字符串,对答案模998244353
解题思路
CF1622D,记录每个1的位置,枚举所有包含k个1的最大窗口len1,累加C(len1,k),对于相邻的窗口会有k-1个1重叠,重叠窗口大小len2,x需要减去重叠的C(len2,k-1),等价于减去所有k-1个1的最大窗口len2,去掉最左和最右窗口
代码实现
#include <bits/stdc++.h>
using i64 = long long;
const int MOD = 998244353;
const int N = 1e5 + 10;
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 tt;
std::cin >> tt;
while (tt--) {
int n, k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
std::vector<int> pos = {-1};
for (int i = 0; i < n; i++) {
if (s[i] == '1') {
pos.push_back(i);
}
}
pos.push_back(n);
i64 ans = 0;
for (int i = 0; i + k + 1 < pos.size(); i++) {
ans = (ans + C(pos[i + k + 1] - pos[i] - 1, k)) % MOD;
}
for (int i = 1; i + k + 1 < pos.size(); i++) {
ans = (ans - C(pos[i + k] - pos[i] - 1, k - 1) + MOD) % MOD;
}
std::cout << ans << "\n";
}
}
E. God's String on This Wonderful World
题目大意
给定一个字符串,问[l,r]有多少个子区间满足每个字符出现的次数都能被k整除
解题思路
问题可以转化为[l-1,r]中有多少个二元组(a,b)满足所有的字母都同余(这样说明区间ab中的字符都是k的倍数),时限3s,数据范围3e5,区间扩展收缩可以计算答案,考虑使用莫队解决
代码实现
#include <bits/stdc++.h>
using i64 = long long;
const int N = 1e6 + 10;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int n, k, q;
std::cin >> n >> k >> q;
int size = std::sqrt(n);
int bnum = ceil((double)n / size);
std::vector<i64> a(n + 1), ch(26), cnt(N), block(n + 1);
for (int i = 1; i <= bnum; i++) {
for (int j = (i - 1) * size + 1; j <= i * size && j <= n; j++) {
block[j] = i;
}
}
std::string s;
std::cin >> s;
s = " " + s;
std::vector<std::array<i64, 3>> ask(q);
for (int i = 0; i < q; i++) {
std::cin >> ask[i][0] >> ask[i][1];
ask[i][0]--;
ask[i][2] = i;
}
std::sort(ask.begin(), ask.end(), [&](std::array<i64, 3> a, std::array<i64, 3> b) {
if (block[a[0]] != block[b[0]]) {
return block[a[0]] < block[b[0]];
}
if (block[a[0]] & 1) {
return a[1] < b[1];
} else {
return a[1] > b[1];
}
});
i64 l = 1, r = 0, idx = 1, now = 0;
std::map<std::vector<i64>, i64> mp;
mp[ch] = 0;
for (int i = 1; i <= n; i++) {
ch[s[i] - 'a'] = (ch[s[i] - 'a'] + 1) % k;
if (!mp.count(ch)) {
mp[ch] = idx++;
}
a[i] = mp[ch];
}
std::vector<i64> ans(q);
for (auto [L, R, idx] : ask) {
while (l > L) {
l--;
now += cnt[a[l]];
cnt[a[l]]++;
}
while (r < R) {
r++;
now += cnt[a[r]];
cnt[a[r]]++;
}
while (l < L) {
cnt[a[l]]--;
now -= cnt[a[l]];
l++;
}
while (r > R) {
cnt[a[r]]--;
now -= cnt[a[r]];
r--;
}
ans[idx] = now;
}
for (int i = 0; i < q; i++) {
std::cout << ans[i] << "\n";
}
}

浙公网安备 33010602011771号