Codeforces Round 973 (Div. 2)
写在前面
比赛地址:https://codeforces.com/contest/2013。
最后 3min 绝杀 E 上分场爽,然而前期太唐了呃呃要是不唐准能红名表现分。
妈的什么时候上黄!
A 签到
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
int n, x, y, ans; std::cin >> n >> x >> y;
if (x < y) {
ans = ceil(1.0 * n / x);
} else {
ans = ceil(1.0 * n / y);
}
std::cout << ans << "\n";
}
return 0;
}
B 结论,贪心
发现第 \(n\) 个人会且一定会和第 \(n-1\) 个人打,则最终的 \(a_n\) 中一定有贡献 \(-a_{n-1}\)。
然后考虑到 \(a_i \ge 1\),则答案的上界即令所有 \(a_1\sim a_{n-2}\) 对 \(a_n\) 的贡献均为正值 \(a_i\)
发现仅需令 \(a_1\sim a_{n-2}\) 先分别和 \(a_{n-1}\) 打,再令 \(a_{n-1}\) 和 \(a_{n}\) 打即可达到上界。则答案即为:
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n;
LL a[kN];
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n;
LL sum = 0;
for (int i = 1; i <= n; ++ i) std::cin >> a[i];
for (int i = 1; i < n - 1; ++ i) sum += a[i];
std::cout << a[n] - a[n - 1] + sum << "\n";
}
return 0;
}
C 交互
考虑每次往后面接一个字符并询问,若为真则固定该位,否则更换字符再询问,直至往后接 0/1 询问都为假。
发现此时的字符串一定为原串的一个后缀,然后考虑再往前不断接一个字符并询问,若为真则固定该位,否则可以保证该位一定为另一个字符。
易证操作数量不超过 \(2n\) 次。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
int n;
std::map <std::string, bool> checked;
//=============================================================
bool query(std::string &t_) {
if (checked.count(t_)) return checked[t_];
std::cout << '?' << " " << t_ << "\n";
std::cout.flush();
bool ret; std::cin >> ret;
return ret;
}
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
// std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n;
std::string s;
checked.clear();
while ((int)s.length() < n) {
s.push_back('0');
bool ret = query(s);
checked[s] = ret;
if (ret) continue;
s.pop_back(), s.push_back('1');
ret = query(s);
checked[s] = ret;
if (ret) continue;
s.pop_back();
break;
}
while ((int) s.length() < n) {
std::string t = "0" + s;
bool ret = query(t);
checked[t] = ret;
if (ret) {
s = t; continue;
}
s = "1" + s;
}
std::cout << '!' << " " << s << "\n";
std::cout.flush();
}
return 0;
}
D 贪心 or 二分答案
由给定的操作可知,显然存在一种最优方案,使得给定数列单调不降。
在这种限制下考虑使所有位置尽可能平均以最小化极差。
考虑对仅前 \(i\) 个位置操作后,其中的最小值的下界(不一定出现在位置 \(i\)),显然为:
考虑仅对后 \(n-i+1\) 个位置操作后,其中的最大值的上界(同样不一定出现在位置 \(i\)),显然为:
然后考虑到对整个数列进行操作,发现对整个数列操作后,可以达到的上下界即对上述的下界取最小值,对上界取最大值,则答案即为:
总时间复杂度 \(O(n)\) 级别。
还有大力二分答案的做法,详见其他题解。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n;
LL a[kN];
//=============================================================
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n;
for (int i = 1; i <= n; ++ i) std::cin >> a[i];
LL s = 0, mina = a[1], maxa = 0;
for (int i = 1; i <= n; ++ i) {
s += a[i], mina = std::min(mina, s / i);
}
s = 0;
for (int i = n; i; -- i) {
s += a[i], maxa = std::max(maxa, (LL) ceil(1.0 * s / (n - i + 1)));
}
std::cout << maxa - mina << "\n";
}
return 0;
}
E 贪心,数学,gcd
发现两个不相等的数 \(x, y\) 取 \(\gcd\),一定有 \(\gcd(x, y)\le \frac{\max(x, y)}{2}\),即每次向序列后面加一个数,前缀的 \(\gcd\) 都至少会除 2,则发现仅需通过不超过 \(\log_2 v\) 个数,即可令前缀 \(\gcd\) 快速地减小到全局 \(\gcd\)。
则显然第一个位置一定放全局最小值,否则根据 \(\gcd\) 的算数基本定理的形式,全局最小值一定对质因数的幂次限制最多,则总可以通过调整使得全局最小值提前,使得总代价更小。
然后考虑每次 \(O(n)\) 地枚举向序列后面加的数并取使 \(\gcd\) 减小的最多的加上去即可,若加上后变为全局 \(\gcd\) 则可直接停止枚举。
总时间复杂度 \(O(n\log n)\) 级别。
赛时根据 \(\sum \max a_i \le 10^5\) 的性质找了一堆优化常数的小结论实际上并无必要呃呃,上面的关键结论出来直接大力做就过了。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
//=============================================================
int n, a[kN];
LL alld, ans;
//=============================================================
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n;
ans = alld = 0;
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
alld = std::__gcd(alld, 1ll * a[i]);
}
std::sort(a + 1, a + n + 1);
int nowd = a[1];
ans += nowd;
for (int i = 2; i <= n; ++ i) {
if (nowd == alld) {
ans += 1ll * alld * (n - i + 1);
break;
}
int newd = nowd;
for (int j = 1; j <= n; ++ j) {
newd = std::min(newd, std::__gcd(nowd, a[j]));
}
nowd = newd, ans += nowd;
}
std::cout << ans << "\n";
}
return 0;
}
/*
1
3
10 15 6
1
5
6 42 12 52 20
*/
F1
什么东西我去
写在最后
学到了什么:
- D:感觉是平均所有位置最小化极差的套路。
- E:gcd 的单调性,最近碰到好几次了呃呃