CSP-J 2025 题解
CSP-J 2025 拼数
要把数字拼成最大的正整数,其实就是把所有数字从大到小排序输出。
代码里用 map 统计数字出现的次数,然后从后往前(逆序)输出即可。因为题目保证至少有一个非零数字,所以完全不用担心开头是 \(0\) 的情况。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string s;
cin >> s;
map<char, int> mp;
for (auto c : s) {
if (isdigit(c)) mp[c]++;
}
for (auto ite = mp.rbegin(); ite != mp.rend(); ite++) {
while (ite->second--) cout << ite->first;
}
cout << '\n';
}
CSP-J 2025 座位
排序求出小R的排名,然后根据蛇形填充规则算出对应的列和行(奇数列正序、偶数列倒序)。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
int N = n * m;
vector<int> a(N);
for (int i = 0; i < N; i++) {
cin >> a[i];
}
int a0 = a[0];
sort(a.begin(), a.end());
int rk = N - (lower_bound(a.begin(), a.end(), a0) - a.begin()) - 1;
int q = rk / n, r = rk % n;
if (q & 1) r = n - 1 - r;
cout << q + 1 << ' ' << r + 1 << '\n';
}
CSP-J 2025 异或和
贪心,从左往右扫,维护当前段的前缀异或集合。每次发现当前前缀异或能和集合中某个值异或得到k,说明找到一个合法段,立刻切割并清空集合重新开始。能切就切是最优的,因为早切不会让后面的选择变少。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
set<int> s;
s.insert(0);
auto s0 = s;
int ans = 0;
for (int i = 0, cur = 0; i < n; i++) {
cur ^= a[i];
if (s.count(cur ^ k)) {
ans++;
cur = 0;
s = s0;
} else {
s.insert(cur);
}
}
cout << ans << '\n';
}
CSP-J 2025 多边形
先排序。排完序后枚举每根木棍 \(a_i\) 作为最大边,前面 \(i\) 根随便选,总共 \(2^i\) 种选法(含空集)。不合法的情况是前面选出来的和 \(\le a_i\),用 01 背包维护前 \(i-1\) 根的子集和分布即可快速统计。
具体来说,设 \(f[j]\) 为前面若干根木棍中选出总和恰为 \(j\) 的方案数,初始 \(f[0]=1\)。对第 \(i\) 根木棍,不合法方案数为 \(\sum_{j=0}^{a_i} f[j]\)(这里 \(j=0\) 对应前面啥都不选,即只有 \(a_i\) 一根,边数不够 3,确实不合法)。合法贡献就是 \(2^i - \sum_{j=0}^{a_i} f[j]\),累加到答案里,然后把 \(a_i\) 扔进背包。
复杂度 \(O(n \cdot \sum a_i)\),\(\sum a_i \le 5000\) 所以随便跑。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int mod = 998244353;
int norm(int x) {
if (x < 0) {
x += mod;
}
if (x >= mod) {
x -= mod;
}
return x;
}
template <class T>
T power(T a, i64 b) {
T res = 1;
for (; b; b /= 2, a *= a) {
if (b % 2) {
res *= a;
}
}
return res;
}
struct Z {
int x;
Z(int _x = 0) : x(norm(_x)) {}
Z(i64 _x) : x(norm(_x % mod)) {}
int val() const { return x; }
Z operator-() const { return Z(norm(mod - x)); }
Z inv() const {
assert(x != 0);
return power(*this, mod - 2);
}
Z& operator*=(const Z& rhs) {
x = i64(x) * rhs.x % mod;
return *this;
}
Z& operator+=(const Z& rhs) {
x = norm(x + rhs.x);
return *this;
}
Z& operator-=(const Z& rhs) {
x = norm(x - rhs.x);
return *this;
}
Z& operator/=(const Z& rhs) { return *this *= rhs.inv(); }
friend Z operator*(const Z& lhs, const Z& rhs) {
Z res = lhs;
res *= rhs;
return res;
}
friend Z operator+(const Z& lhs, const Z& rhs) {
Z res = lhs;
res += rhs;
return res;
}
friend Z operator-(const Z& lhs, const Z& rhs) {
Z res = lhs;
res -= rhs;
return res;
}
friend Z operator/(const Z& lhs, const Z& rhs) {
Z res = lhs;
res /= rhs;
return res;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a.begin(), a.end());
vector<Z> f(5001); // f[i] 表示和为 i 的合法方案
f[0] = 1;
Z ans = 0;
for (int i = 0; i < n; i++) {
Z invalid = 0;
for (int j = 0; j <= a[i]; j++) invalid += f[j];
ans += power(Z(2), i) - invalid;
// 本身 a[i] 也放进背包
for (int j = 5000; j >= a[i]; j--) f[j] += f[j - a[i]];
}
cout << ans.val() << '\n';
}
这里用 power 的时候记得给 2 也套个 Z,jiangly 模板用的还是不熟练

浙公网安备 33010602011771号