https://codeforces.com/gym/105336
B - 軍訓 II
排序後肯定是最優解,方案數就是能排成有序序列的個數
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 1e9, INF = 1e18;
const i64 mod = 998244353;
struct mint {
i64 x;
mint(i64 x = 0) : x(x) {};
mint &operator=(i64 o) { return x = o, *this; }
mint &operator+=(mint o) { return (x += o.x) >= mod && (x -= mod), *this; }
mint &operator-=(mint o) { return (x -= o.x) < 0 && (x += mod), *this; }
mint &operator*=(mint o) { return x = (x * o.x) % mod, *this; }
mint &operator^=(int b) {
mint w = *this, ret(1);
for (; b; b >>= 1, w *= w) if (b & 1) ret *= w;
return x = ret.x, *this;
}
mint &operator/=(mint o) { return *this *= (o ^= (mod - 2)); }
friend mint operator+(mint a, mint b) { return a += b; }
friend mint operator-(mint a, mint b) { return a -= b; }
friend mint operator*(mint a, mint b) { return a *= b; }
friend mint operator/(mint a, mint b) { return a /= b; }
friend mint operator^(mint a, i64 b) { return a ^= b; }
i64 val() {
x = (x % mod + mod) % mod;
return x;
}
};
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
vi a(n);
for (auto &i: a) cin >> i;
ranges::sort(a, greater<>());
int res = 0;
for (int cnt = n, sum = accumulate(a.begin(), a.end(), 0); auto i: a) {
cnt--, sum -= i;
res += cnt * i - sum;
}
cout << res;
vector<mint> fact(n + 1);
fact[0] = 1;
for (int i = 1; i <= n; i++)
fact[i] = fact[i - 1] * i;
mint ret(2 - (a.back() == a.front()));
a.push_back(-1);
for (int lst = -1, cnt = 0; auto i: a) {
if (lst != i) {
ret *= fact[cnt], cnt = 1, lst = i;
} else {
cnt++;
}
}
cout << " " << ret.val();
return 0;
}
D - 編碼器-解碼器
\(f[i][l][r]\)表示\(S_i\)可以匹配\(T[l,r]\)的方案數,然後按照題目的意思進行\(O(N^4)\)的轉移就好了。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 1e9, INF = 1e18;
const i64 mod = 998244353;
struct mint {
i64 x;
mint(i64 x = 0) : x(x) {};
mint &operator=(i64 o) { return x = o, *this; }
mint &operator+=(mint o) { return (x += o.x) >= mod && (x -= mod), *this; }
mint &operator-=(mint o) { return (x -= o.x) < 0 && (x += mod), *this; }
mint &operator*=(mint o) { return x = (x * o.x) % mod, *this; }
mint &operator^=(int b) {
mint w = *this, ret(1);
for (; b; b >>= 1, w *= w) if (b & 1) ret *= w;
return x = ret.x, *this;
}
mint &operator/=(mint o) { return *this *= (o ^= (mod - 2)); }
friend mint operator+(mint a, mint b) { return a += b; }
friend mint operator-(mint a, mint b) { return a -= b; }
friend mint operator*(mint a, mint b) { return a *= b; }
friend mint operator/(mint a, mint b) { return a /= b; }
friend mint operator^(mint a, i64 b) { return a ^= b; }
i64 val() {
x = (x % mod + mod) % mod;
return x;
}
};
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
string s, t;
cin >> s >> t;
int n = s.size(), m = t.size();
vector f(n, vector(m + 1, vector(m + 1, mint())));
for (int i = 0; i < m; i++)
if (s[0] == t[i]) f[0][i][i] = 1;
for (int i = 1; i < n; i++)
for (int l = 0; l < m; l++)
for (int r = l; r < m; r++) {
f[i][l][r] = f[i - 1][l][r] * 2;
if (l == r) {
if (s[i] == t[l]) f[i][l][r] += 1;
} else {
if (s[i] == t[l]) f[i][l][r] += f[i - 1][l + 1][r];
if (s[i] == t[r]) f[i][l][r] += f[i - 1][l][r - 1];
for (int k = l; k < r; k++)
f[i][l][r] += f[i - 1][l][k] * f[i - 1][k + 1][r];
for (int k = l + 1; k < r; k++)
if (s[i] == t[k])
f[i][l][r] += f[i - 1][l][k - 1] * f[i - 1][k + 1][r];
}
}
cout << f[n - 1][0][m - 1].val();
return 0;
}
E. 隨機過程
最大點數就是使得每一層儘可能的填滿。
對於期望點數,同一層的點是相互獨立的。每層能夠出現的點有\(26^i\)種,對於某一種點,被選到的機率就是\(\frac{1}{26^i}\),那麼選不到的機率就是\(1 - \frac{1}{26^i}\),我們要選擇\(n\)次,\(n\)次都選不到的機率是\((1- \frac{1}{26^i})^ n\),所以這個點被選中的機率就是\(1 - (1- \frac{1}{26^i})^ n\)。這層的點共有\(26^i\)個,因此總的期望就是\(26^i(1 - (1- \frac{1}{26^i})^ n)\),共\(m\)層,所以答案就是
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using ui32 = uint32_t;
using i64 = long long;
//#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const i64 mod = 998244353;
struct mint {
i64 x;
mint(i64 x = 0) : x(x) {}
mint &operator=(i64 o) { return x = o, *this; }
mint &operator+=(mint o) { return (x += o.x) >= mod && (x -= mod), *this; }
mint &operator-=(mint o) { return (x -= o.x) < 0 && (x += mod), *this; }
mint &operator*=(mint o) { return x = (i64) x * o.x % mod, *this; }
inline mint &operator^=(int b) {
mint w = *this;
mint ret(1);
for (; b; b >>= 1, w *= w) if (b & 1) ret *= w;
return x = ret.x, *this;
}
mint &operator/=(mint o) { return *this *= (o ^= (mod - 2)); }
friend mint operator+(mint a, mint b) { return a += b; }
friend mint operator-(mint a, mint b) { return a -= b; }
friend mint operator*(mint a, mint b) { return a *= b; }
friend mint operator/(mint a, mint b) { return a /= b; }
friend mint operator^(mint a, int b) { return a ^= b; }
i64 val() {
x = (x % mod + mod) % mod;
return x;
}
};
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, m;
cin >> n >> m;
const mint b(26), one(1);
mint sum;
for (int i = 0, x = 1, t = 26; i <= m; i++) {
sum += x;
x *= t;
if (x > n) x = n, t = 1;
}
mint res;
for (int i = 0; i <= m; i++) {
mint t = b ^ i;
res += t * (one - ((one - (one / t)) ^ n));
}
cout << sum.val() << " " << res.val();
return 0;
}
J. 找最小
首先我們可以求出原本的異或和\(A,B\),再求出\(c_i = a_i \oplus b_i\)
此時我們就可以考慮用\(c_i\)選擇一些數去異或,看能否使得答案減小。
我們貪心的從高位開始操作。
如果當前位\(A,B\)都是\(1\),能操作就操作。
如果當前位都是\(0\),不需要任何操作。
如果當前位的較大數是\(1\),我們可以考慮操作。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using ui32 = uint32_t;
using i64 = long long;
//#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
#define lowbit(x) ( x & -x )
struct Basis {
vector<ui32> B;
Basis() {
B = vector<ui32>();
}
void insert(ui32 x) {
for (auto b: B)
x = min(x, b ^ x);
if (x == 0) return;
for (auto &b: B)
b = min(b, b ^ x);
B.push_back(x);
return;
}
ui32 find(ui32 x) {
for (auto i: B) {
int y = i;
while (lowbit(y) != y) y -= lowbit(y);
if (x == y) return i;
}
return 0;
}
};
void solve() {
int n;
cin >> n;
vector<ui32> a(n), b(n);
ui32 A = 0, B = 0;
for (auto &i: a) cin >> i, A ^= i;
for (auto &i: b) cin >> i, B ^= i;
ui32 res = max(A, B);
Basis basis;
for (int i = 0; i < n; i++)
basis.insert(a[i] ^ b[i]);
for (ui32 i = (1 << 30); i > 0; i >>= 1) {
if (A < B) swap(A, B);
if (((A & i) == 0) and ((B & i) == 0)) continue;
if (A & i) {
ui32 x = basis.find(i);
A ^= x, B ^= x;
}
}
res = min(res, max(A, B));
cout << res << "\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
K - 取沙子游戲
這道題目是賽時先寫出SG函式,然後找規律發現。
- \(n\)為奇數先手必勝,因為只要先手拿\(1\)後手就只能拿\(1\)
- \(k \ge n\)先手必勝,因為先手可以一步全部拿完。
- 剩下的情況透過找規律發現,對於\(n\)找到最大的\(t\)滿足\(t | n\) 且\(t\)為 \(2\)的整次冪,那麼\(t \ge k\) 先手必勝
原因賽時沒想出來
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 1e9, INF = 1e18;
void solve() {
int n, k;
cin >> n >> k;
if (n & 1) {
cout << "Alice\n";
} else if (k >= n) {
cout << "Alice\n";
} else {
int p = 1;
while (n % (p * 2) == 0) p *= 2;
if (k < p) cout << "Bob\n";
else cout << "Alice\n";
}
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
賽後看了題解,題解總結出來一條規律就是\(\mathrm{lowbit}(n) \le k\)先手必勝。
如果\(\mathrm{lowbit}(n) \le k\),先手可以取一個\(\mathrm {lowbit}(n)\),然後後手一定無法一步取完,這種情況其實和 Nim 博弈很類似。道理上來說我打表找到的規律實際上是殊途同歸了
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 1e9, INF = 1e18;
#define lowbit(x) ( x & -x )
void solve() {
int n, k;
cin >> n >> k;
if (lowbit(n) > k) cout << "Bob\n";
else cout << "Alice\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
L - 網路預選賽
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 1e9, INF = 1e18;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<string> g(n);
for (auto &i: g) cin >> i;
int res = 0;
for (int i = 0; i + 1 < n; i++)
for (int j = 0; j + 1 < m; j++) {
if (g[i][j] == 'c' and g[i][j + 1] == 'c' and g[i + 1][j] == 'p' and g[i + 1][j + 1] == 'c')
res++;
}
cout << res;
return 0;
}