比赛链接:
https://codeforces.com/gym/104090
A. Modulo Ruins the Legend
题意:
给定一个序列 \(a\),让序列加上一个等差序列,求出总和 % \(m\) 的最小值以及等差序列的 \(s\) 和公差 \(d\)。
思路:
定义 \(\sum_{i = 1}^n a_i\) 为 \(sum\)。
求解的答案为 \((sum + n * s + \frac{n * (n + 1)}{2} * d) \% m\) 的最小值。
根据裴蜀定理得到原式等于 \(sum + x * gcd(n, \frac{n * (n + 1)}{2})\)
加上一个 \(y * m\) 不影响结果(因为对 \(m\) 取模),\(sum + x * gcd(n, \frac{n * (n + 1)}{2}) + y * m\)
再裴蜀,\(sum + k * g\)。
求该值的最小,这个值大于 0,所以最小应该是等于 \(m\),得到 \(k = \lceil \frac{sum - m}{g} \rceil\)。
通过扩展欧几里得求解值。
代码:
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
LL exgcd(LL a, LL b, LL &x, LL &y){
if (!b){
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= ( a / b ) * x;
return d;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL n, m;
cin >> n >> m;
LL sum = 0;
for (int i = 0; i < n; i ++ ){
int x;
cin >> x;
sum = (sum + x) % m;
}
LL s, d, x, y;
LL g = exgcd(exgcd(n, n * (n + 1) / 2, s, d), m, x, y);
LL k = (m - sum + g - 1) / g;
cout << k * g + sum - m << "\n";
x = x * k % m;
cout << (s * x % m + m) % m << " " << (d * x % m + m) % m << "\n";
return 0;
}
C. No Bug No Game
题意:
\(n\) 件装备,第 \(i\) 件装备负重为 \(p\),人物初始的负重为 0。
如果当前负重为 \(x\),当 \(x + p_i \leq k\),可获得 w_{i, p_i} 的奖励值。当 \(x + p_i > k\),获得 \(w_{i, k - p_i}\) 的奖励值,问最多能获得多少奖励值。
思路:
当装备总负重 \(\leq k\),答案为 \(\sum_{i = 1}^n w_{i, p_i}\)。
否则,可以发现,除了一件装备,其它选择的装备获得的奖励值都会是 \(w_{i, p_i}\)。
因此可以设 \(f_{i, j, 0}\) 前 \(i\) 件装备,负重为 \(j\),没有选择超过负重的装备的最大奖励值,1 则表示有选择。
代码:
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int n, k;
cin >> n >> k;
const int inf = 1e9;
vector<vector<array<int, 2>>> f(n + 10, vector<array<int, 2>>(k + 10, {-inf, -inf}));
int tot = 0, sum = 0;
f[0][0] = {0, 0};
for (int i = 1; i <= n; i ++ ){
int p;
cin >> p;
tot += p;
vector<int> w(p + 1);
for (int j = 1; j <= p; j ++ ){
cin >> w[j];
}
sum += w[p];
for (int j = 0; j <= k; j ++ ){
f[i][j] = f[i - 1][j];
if (j >= p){
f[i][j][0] = max(f[i][j][0], f[i - 1][j - p][0] + w[p]);
f[i][j][1] = max(f[i][j][1], f[i - 1][j - p][1] + w[p]);
}
for (int t = 1; t < p; t ++ ){
if (j >= t){
f[i][j][1] = max(f[i][j][1], f[i - 1][j - t][0] + w[t]);
}
}
}
}
if (tot <= k){
cout << sum << "\n";
return 0;
}
cout << max(f[n][k][0], f[n][k][1]) << "\n";
return 0;
}
D. Money Game
题意:
\(n\) 个人玩游戏,第 \(i\) 个人的存款为 \(a_i\),每一轮第一个人将自己一半的存款给第二个人,然后第二个人将自己一半的存款给第三个人...,第 \(n\) 个人将自己一半的存款给第一个人,问无限轮之后每个人手上的存款有多少。
思路:
暴力跑一定轮次可找出规律。
代码:
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
ios::sync_with_stdio(false);cin.tie(0);
cout << fixed << setprecision(10);
int n;
cin >> n;
double sum = 0;
for (int i = 0; i < n; i ++ ){
int x;
cin >> x;
sum += x;
}
sum /= n + 1;
for (int i = 0; i < n; i ++ ){
if (i) cout << sum;
else cout << sum * 2;
cout << " \n"[i == n - 1];
}
return 0;
}
F. Da Mi Lao Shi Ai Kan De
题意:
\(n\) 个小组,第 \(i\) 个小组有 \(m_i\) 个字符串,输出其中包含 "bie" 的字符串,如果该小组没有,输出 "Time to play Genshin Impact, Teacher Rice!"。
思路:
按题意模拟。
代码:
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int n;
cin >> n;
map<string, int> cnt;
for (int i = 0; i < n; i ++ ){
int m;
cin >> m;
bool ok = false;
for (int j = 0; j < m; j ++ ){
string s;
cin >> s;
if (s.find("bie") != -1 && !cnt.count(s)){
cnt[s] = 1;
cout << s << "\n";
ok = true;
}
}
if (!ok){
cout << "Time to play Genshin Impact, Teacher Rice!\n";
}
}
return 0;
}
K. Master of Both
题意:
给定 \(n\) 个字符串,\(q\) 次询问,每次给定 26 个字母的字典序大小,问字符串中有几个逆序对。
思路:
判断两个字符串是不是逆序的,去掉它们的公共前缀,比较大小,因此,是否是逆序由字符串中第一个不同的字符决定。
定义 \(f_{a, b}\) 为 \(n\) 个字符串中只有当 \(a > b\) 时,逆序对的数量。
字符串第 \(i\) 位为 \(u\),下一位为 \(v\),\(f_{j, v}\) 可以加上所有对应 \(v\) 的位置为 \(j\) 的字符串。
统计字符串数量以及计算 \(f\) 数组可以通过 \(Trie\)。
为了处理 \(ab\),\(abc\) 这种某个字符串为另一个前缀的情况,加入一个新的字符 'a' - 1,它小于 26 个字母。
代码:
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 1e6 + 10;
LL f[27][27];
int ch[N][27], cnt[N * 30], idx = 0;
void insert(string s){
int u = 0;
for (int i = 0; i < s.size(); i ++ ){
int v = s[i] - 'a' + 1;
if (!ch[u][v]) ch[u][v] = ++ idx;
for (int j = 0; j < 27; j ++ ){
if (j == v) continue;
f[j][v] += cnt[ch[u][j]];
}
u = ch[u][v];
cnt[u] ++ ;
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int n, q;
cin >> n >> q;
for (int i = 0; i < n; i ++ ){
string s;
cin >> s;
s = s + char('a' - 1);
insert(s);
}
while (q -- ){
string s;
cin >> s;
s = char('a' - 1) + s;
LL ans = 0;
for (int i = 1; i < 27; i ++ ){
for (int j = 0; j < i; j ++ ){
ans += f[s[i] - 'a' + 1][s[j] - 'a' + 1];
}
}
cout << ans << "\n";
}
return 0;
}