2025 ICPC 南京区域赛 CFGIJ
C. Distributing Candies
思维。
奇数无论怎么分都会分出一个偶数,而奇数不能整除偶数;偶数的话对半分即可。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
i64 n;
std::cin >> n;
if(n & 1) {
std::cout << "No\n";
return;
}
std::cout << "Yes\n";
std::cout << n / 2 << " " << n / 2 << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
F. Bitwise And Path
并查集。
按每个值都去建一个并查集,当 \(u, v\) 加 \(w\) 的边时,对 \(w\) 及其子集都进行合并,查询时按位从高到低检查即可,有点类似字典树的思想。
复杂度理论上最坏是 \(2^{12}\times 10^6\),但是在枚举子集的时候很多都是会重合的,直接跳过即可,所以远远到不了上界。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
void solve() {
int n, q;
std::cin >> n >> q;
std::vector dsu(1 << 12, DSU(n));
auto dfs = [&](auto &&self, int u, int v, int w)->void{
if(dsu[w].same(u, v)) {
return;
}
dsu[w].merge(u, v);
for(int i = 11; i >= 0; i -= 1) {
if(w >> i & 1) {
self(self, u, v, w ^ (1 << i));
}
}
};
i64 ans = 0;
while(q--) {
char op;
int u, v, w;
std::cin >> op >> u >> v;
u --, v --;
if(op == '+') {
std::cin >> w;
dfs(dfs, u, v, w);
} else {
if(!dsu[0].same(u, v)) {
ans += -1;
continue;
}
int res = 0;
for(int i = 11; i >= 0; i -= 1) {
if(dsu[res | 1 << i].same(u, v)) {
res |= 1 << i;
}
}
ans += res;
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
G. Bucket Bonanza
贪心。
虽然能合并多个,但只取最大容量和最小流速,显然只合并那两即可,其他的多合没意义。
令 \(G = v - t\times l\),什么时候两个配对是一定贡献增大的 \(?\) 设 \(G_i \ge 0, G_j \ge 0,v_j > v_i\),那么合并当且仅当 $v_j-tl_i > v_i-tl_i + v_j-tl_j $ 即 \(v_i<tl_j\) 时,选择 \(i,j\) 合并更好。
假设刚开始所有 \(G\ge 0\),那么说明 \(\sum tl_i\) 是必须要丢掉的,如果存在 \(v_i<tl_j\),那么可以让 \(i,j\) 合并,可以理解为让这个小容量去匹配这个大流速的,那么我们就少丢掉了 \(tl_j-v_i\) 的部分,这样显然是更优的。
具体的,对体积维护最小,流速维护最大,然后将询问离线后从小到大按上面所说处理即可。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
i64 V = 0;
std::priority_queue<i64,std::vector<i64>,std::greater<i64>> q;
for(int i = 0; i < n; i += 1) {
i64 x;
std::cin >> x;
V += x;
q.push(x);
}
i64 S = 0;
std::priority_queue<i64> p;
for(int i = 0; i < n; i += 1) {
int x;
std::cin >> x;
S += x;
p.push(x);
}
int m;
std::cin >> m;
std::vector<std::array<int,2>> Q(m);
for(int i = 0; i < m; i += 1) {
std::cin >> Q[i][0];
Q[i][1] = i;
}
sort(Q.begin(), Q.end());
std::vector<i64> ans(m);
for(auto &[t, id] : Q) {
while(p.size() && q.size() && p.top() * t >= q.top()) {
S -= p.top();
p.pop();
V -= q.top();
q.pop();
}
ans[id] = V - S * t;
}
for(int i = 0; i < m; i += 1) {
std::cout << ans[i] << " \n"[i + 1 == m];
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
I. Chi Fan
期望 \(dp\)。
因为需要最坏情况下不能超过预算,所以要保证后面预算足够情况下求得最大满意度期望,可以倒着 \(dp\)。
设 \(dp_{i,j,k,p}\) 表示为第 \(i\) 天开始之前还有 \(p\) 元的预算,并且 \(A/B\) 是否付过了。
如果当天在食堂吃饭,转移有 \(dp_{i,j,k,p}\leftarrow \max(dp_{i,j,k,p}, a+P_i\times dp_{i+1,1,k,p-b}+(1-P_i)\times dp_{i+1,j,1,p-b})\)。
不在食堂吃饭,如果这之前 \(A,B\) 都付过了,那么这次是一定要付的,转移有 \(dp_{i,j,k,p}\leftarrow \max(dp_{i,j,k,p}, c+ dp_{i+1,0,0,p-d-e})\)。
否则他两付钱,转移有 \(dp_{i,j,k,p}\leftarrow \max(dp_{i,j,k,p}, c+P_i\times dp_{i+1,1,k,p-d}+(1-P_i)\times dp_{i+1,j,1,p-d})\)。
最后答案就是 \(dp_{1,0,0,m}\)。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
using d64 = double;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
std::cin >> n >> m;
std::vector<std::array<int,6>> ve(n);
for(auto &[a, b, c, d, e, p] : ve) {
std::cin >> a >> b >> c >> d >> e >> p;
}
const i64 inf = 1E18;
std::vector dp(2,std::vector(2, std::vector<d64>(m + 1)));
for(int i = n - 1; i >= 0; i -= 1) {
std::vector ndp(2, std::vector(2, std::vector<d64>(m + 1, -inf)));
auto &[a, b, c, d, e, p] = ve[i];
d64 P = p * 1.L / 100;
for(int i = 0; i < 2; i += 1) {
for(int j = 0; j < 2; j += 1) {
for(int k = 0; k <= m; k += 1) {
if(dp[i][j][k] < 0) continue;
if(k >= b) {
ndp[i][j][k] = std::max(ndp[i][j][k], a + P * dp[1][j][k - b] + (1 - P) * dp[i][1][k - b]);
}
if(i && j) {
if(k >= d + e) {
ndp[i][j][k] = std::max(ndp[i][j][k], dp[0][0][k - d - e] + c);
}
} else {
if(k >= d) {
ndp[i][j][k] = std::max(ndp[i][j][k], c + P * dp[1][j][k - d] + (1 - P) * dp[i][1][k - d]);
}
}
}
}
}
dp = std::move(ndp);
}
auto ans = dp[0][0][m];
if(ans < 0) {
std::cout << -1 << "\n";
} else {
std::cout << std::fixed << std::setprecision(10) << ans << "\n";
}
return 0;
}
K. Xiangqi
\(Guess?\)
比较神秘,刚开始是判断在四个角落或者四个边上就可以,但 WA 了,后续是写了个马只要能第一步能跳不在和车同行同列的地方就能来回循环永远躲掉车。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
const int u[] = {2, 2, -2, -2, 1, -1, 1, -1};
const int v[] = {1, -1, 1, -1, 2, 2, -2, -2};
const int du[] = {1, 1, -1, -1, 0, 0, 0, 0};
const int dv[] = {0, 0, 0, 0, 1, 1, -1, -1};
int x1, y1, x2, y2;
std::cin >> x1 >> y1 >> x2 >> y2;
for(int i = 0; i < 8; i += 1) {
if(x2 == x1 + du[i] && y2 == y1 + dv[i]) continue;
int dx = x1 + u[i], dy = y1 + v[i];
if(dx >= 1 && dx <= 9 && dy >= 1 && dy <= 10 && x2 != dx && y2 != dy) {
std::cout << "NO\n";
return;
}
}
std::cout << "YES\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号