Codeforces-Round-880-A---C-&-Codeforces-Round-882-A---D
A. a
思路:可以发现 | ai - ai + 1 | 是两个元素差的绝对值,分成m段,也就是有 m - 1个绝对值不用计算,所以只需要除去 m - 1 个最大差值外,把剩余的值相加。
#include <bits/stdc++.h>
#define int long long
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> a(n), t;
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
int ans = 0;
for (int i = 1; i < n; i++) {
t.push_back(abs(a[i] - a[i - 1]));
ans += t.back();
}
sort(t.begin(), t.end(), std::greater<int>());
for (int i = 0; i < m - 1; i++) {
ans -= t[i];
}
std::cout << ans << '\n';
}
signed main() {
int t = 1;
std::cin >> t;
while (t--) solve();
return 0;
}
B. b
思路: 我们需要知道显示的数少的机器人数一定不能少于显示的数多的机器人数,那么我们只需要判断每一种数有多少个即可, 在判断是否出现跳步
#include <bits/stdc++.h>
#define int long long
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
std::map<int, int> mp;
for (int i = 0; i < n; i++) {
std::cin >> a[i];
mp[a[i]]++;
}
int t = -1, x = INT_MAX;
for (auto [k, v] : mp) {
if (t + 1 != k || v > x) {
std::cout << "NO\n";
return;
}
t = k;
x = v;
}
std::cout << "YES\n";
}
signed main() {
int t = 1;
std::cin >> t;
while (t--) solve();
return 0;
}
C. c
思路: 简单思考可以知道,当只有一段时,其最终的值肯定是最小的,若取该值为 t。所以我们从第一个元素开始计算,当值为t时,将这些元素分为第一段。因为 t + 0 = t,所以我们后几段的值都需要为0,所以每出现一个值为0的段,则该段为符合的新段。
#include <bits/stdc++.h>
#define int long long
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
int t = -1; // -1的二进制为全 1
for (int i = 0; i < n; i++) {
std::cin >> a[i];
t &= a[i];
}
// 最终结果不为0 直接输出1
if (t) {
std::cout << "1\n";
return;
}
int x = -1, ans = 0;
for (int i = 0; i < n; i++) {
x &= a[i];
if (x == t) {
ans++;
x = -1;
}
}
std::cout << ans << '\n';
}
signed main() {
int t = 1;
std::cin >> t;
while (t--) solve();
return 0;
}
D. d
思路:贪心地给每个人先分 t = ⌈g / 2⌉ − 1 个银币,这样使得每个人不会获得的部分是最多的。
设 p 为每个人都发 ⌈g / 2⌉ − 1 个银币最多省下的银币
最后还剩下 k * g - p 个, 因为一开始已经给所有人分了不会获得的上限,因此对于任意一个人,只要多一个银币就会多分一个金 需要分 f = (k * g - p + g - 1) / g 个金
结果就是 k * g - f * g
#include <bits/stdc++.h>
#define int long long
void solve() {
int n, k, g;
std::cin >> n >> k >> g;
int t = (g + 1) / 2 - 1;
int p = std::min(n * t, k * g);
int f = ((k * g - p) + g - 1) / g;
int ans = (k - f) * g;
std::cout << ans << '\n';
}
signed main() {
int t = 1;
std::cin >> t;
while (t--) solve();
return 0;
}
E. e
思路: 异或运算的性质, 两个相同的数异或结果 为 0
这个题就是在求 数组中任意一段连续序列异或和的最大值: 【L , R】的异或和 相当于在数组的末尾加上一个 【L ,N】 异或和的结果, 然后求【L, N + 1】的异或和 ,L⊕(L+1)⊕(L+2)⊕⋯⊕R = R⊕(R+1)⊕(R+2)⊕⋯⊕N ⊕ (N + 1) , N + 1 = (L⊕(L+1)⊕(L+2)⊕⋯⊕N)
如何找一段连续区间的异或和最大值呢, 如果枚举每个区间的话复杂度是 n^ 2的,显然会超时,
注意到数据的范围是< 2 ^ 8次, 可以使用set去重左区间,也可以dp, 两种方法本质上一样
代码一:set去重
#include <bits/stdc++.h>
#define int long long
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
int ans = 0, res = 0;
std::set<int> se; // 对左区间进行去重
for (int i = 0; i < n; i++) {
res ^= a[i];
ans = std::max(ans, res);
// 不需要枚举每一个左区间
for (auto it : se) {
ans = std::max(ans, it ^ res);
}
se.insert(res);
}
std::cout << ans << '\n';
}
signed main() {
int t = 1;
std::cin >> t;
while (t--) solve();
return 0;
}
代码二 dp
#include <bits/stdc++.h>
#define int long long
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
std::vector<int> cnt(1 << 8, 0);
int ans = 0;
for (auto x : a) {
ans = std::max(ans, x);
std::vector<int> tem(1 << 8, 0);
for (int u = 0; u < 1 << 8; u++) {
if (cnt[u]) {
tem[u ^ x]++;
ans = std::max(ans, u ^ x);
}
}
cnt = tem;
cnt[x]++;
}
std::cout << ans << '\n';
}
signed main() {
int t = 1;
std::cin >> t;
while (t--) solve();
return 0;
}
F. f
思路: 观察数据范围, 可以枚举 A, 计算 C 的范围 l - r,求第 k 个即可
#include <bits/stdc++.h>
#define int long long
void solve() {
int a, b, c, k;
std::cin >> a >> b >> c >> k;
int l1 = pow(10, a - 1), r1 = pow(10, a) - 1;
int l2 = pow(10, b - 1), r2 = pow(10, b) - 1;
int l3 = pow(10, c - 1), r3 = pow(10, c) - 1;
for (int i = l1; i <= r1; i++) {
int l = std::max(i + l2, l3);
int r = std::min(i + r2, r3);
if (l > r) {
continue;
}
if (r - l + 1 >= k) {
int ans = l + k - 1;
std::cout << i << " + " << ans - i << " = " << ans << '\n';
return;
}
k -= (r - l + 1);
}
std::cout << "-1\n";
}
signed main() {
int t = 1;
std::cin >> t;
while (t--) solve();
return 0;
}
G. g
思路: 贪心 + 并查集 / set + 树状数组
子串靠前的,位置在子串中靠前的,优先级更大。因此,按子串顺序,子串内从左到右,给 s 的位置规定排名 rk 。
排名中可能会遇到之前已经排名过的位置,这些位置的排名是不需要改变的,我们需要跳过它们。为了保证复杂度,可以用set或 并查集 实现跳转。
接下来,我们要尽可能把 1 交换到排名靠前的位置,因此我们需要知道 1 的总数 cnt ,以及排名前 cnt 个位置的 1 个数 sum[1,cnt] ,交换次数就是 cnt−sum[1,cnt] 。
#include <bits/stdc++.h>
#define int long long
class BT {
private:
std::vector<int> tree;
public:
BT(int n) : tree(n + 1) {}
void add(int i, int val) {
while (i < tree.size()) {
tree[i] += val;
i += i & -i;
}
}
int get(int i) {
int res = 0;
while (i > 0) {
res += tree[i];
i &= i - 1;
}
return res;
}
int query(int l, int r) {
return get(r) - get(l - 1);
}
};
void solve() {
int n, m, q;
std::cin >> n >> m >> q;
std::string s;
std::cin >> s;
s = ' ' + s;
int cnt = 0;
for (auto ch : s) cnt += (ch == '1');
std::set<int> se; // 维护未被处理的点
for (int i = 1; i <= n + 1; i++) {
se.insert(i);
}
std::vector<int> rk(n + 1);
int idx = 0;
BT t(n);
for (int i = 0; i < m; i++) {
int l, r;
std::cin >> l >> r;
auto it = se.lower_bound(l);
while (it != se.end() && *it <= r) {
int j = *it;
rk[j] = ++idx;
if (s[j] == '1') {
t.add(rk[j], 1);
}
it = se.erase(it); // 标记为已处理
}
}
while (q--) {
int x;
std::cin >> x;
if (s[x] == '0') {
s[x] = '1';
cnt++;
if (rk[x]) t.add(rk[x], 1);
} else {
s[x] = '0';
cnt--;
if (rk[x]) t.add(rk[x], -1);
}
int ans = std::min(cnt, idx);
std::cout << ans - t.get(ans) << '\n';
}
}
signed main() {
int t = 1;
// std::cin >> t;
while (t--) solve();
return 0;
}
浙公网安备 33010602011771号