Codeforces Round 882 (Div. 2)

Codeforces Round 882 (Div. 2)

A.The Man who became a God

题目大意

给定一个数组 x1,x2,,xn 和一个整数 k,记 f(l,r)=i=0irlxl+ixl+i+1,求将数组划分为 k 个部分的划分方案,使得对每个部分的 f(l,r) 之和最小.

思路

求出相邻两个元素的差值,去掉前 m 个大的差值以后的差值和为答案

memset(b, 0, sizeof(b)); int n, k; cin >> n >> k; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i < n; i++) b[i] = abs(a[i] - a[i + 1]); sort(b + 1, b + n + 1); int ans = 0; for (int i = 1; i <= n - k + 1; i++) ans += b[i]; cout << ans << endl;

B Hamon Odyssey

题目大意

长长长......link

思路

首先我们已知

min(a,b)a&b

所以每两个数一直两两相与一定会形成一个不下降序列,易得 a1&a2&...&an 为最题意中的小值

因为这个值是最小值,所以当这个数 >0 时,使用其他分组的答案和一定比这个答案大,所以只能分一组,也就是一整组为一组

当最小值为 0 时,我们考虑从 a1 一直遍历到 an ,一定会有一种情况使得 a1&a2...&ai 的值为 0 ,因为保底到 an

当出现为 0 的情况时,就切一刀,分一组,因为要尽量分多组,且 0 已经是最小的了。

#include <bits/stdc++.h> using namespace std; int t, n; int a[200004]; int ans, minn, an = -1; int main() { cin >> t; while (t--) { int n; cin >> n >> a[1]; int minn = a[1]; for (int i = 2; i <= n; i++) cin >> a[i], minn = minn & a[i]; if (minn > 0) { puts("1"); continue; } int an = -1, ans = 0; for (int i = 1; i <= n; i++) { if (an == -1) an = a[i]; an &= a[i]; if (an == 0) { an = -1; ans++; } } cout << ans << endl; } return 0; }

C.Vampiric Powers, anyone?

题目大意

link

思路

我们发现对于 l,r 我们先取 r 再取 l 进行操作,然后我们就发现, r+1n 的位置都无效了,因为 ABB=A ,r+1n 异或了两次,自然无效

所以我们可以把问题转化为

给定一个序列 a ,求 a 中的区间最大异或和

很容易想到 dp,我们令 dpi,j 为以 i 为结尾的区间知否有可能异或和为 j

边界条件:dp0,0=1,dpi,ai=1

状态转移方程

dpi,jai|=dpi1,j

很容易想到,将那个区间再添加一个 ai 的值就行了

我们枚举所有可能的 j (也就是所有状态) 即可

#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; bool dp[N][1 << 8 | 1]; int a[N]; int main() { int T; cin >> T; while (T--) { int n; cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; dp[0][0] = 1; int ans = 0; for (int i = 1; i <= n; i++) { for (int j = 0; j < (1 << 8); j++) dp[i][j] = 0; for (int j = 0; j < (1 << 8); j++) { dp[i][j ^ a[i]] |= dp[i - 1][j]; if (dp[i][j]) ans = max(ans, j); } dp[i][a[i]] = 1; ans = max(ans, a[i]); } cout << ans << endl; } return 0; }

D. Professor Higashikata

题目大意

经过某个 s 串中的子串拼接,会得到一个 t 串,为了使得 t 串的字典序最大,要对 s 串中的字符进行几次交换,输出最下的次数?

思路

1.

首先我们知道,为了让 t 串最大,其实就是让 t 串t通过交换使得其中靠前的位置尽量变成 1

要想知道怎样交换最好,我们就要知道 t 串中用了 s 中的那些字符

我们发现在构成 t 串的 m 个子串拼接过程中,明显会有产生交集,所以我们要考虑区间的合并操作。(并查集跳跃搜索法)

2.

然后我们接下来想:怎么样才能让交换次数最小?

我们定义两个变量来方便我们操作

cnt :表示 S 串中 1 的个数

tot :合并区间后的 t 串的字符总数

3. 我们对 cnttot 的情况进行分类讨论

cnttot

这个状态表明每个为 0 的数都可以被换满,交换次数就是合并区间后 t0 的个数

最小交换次数即为 :tott1

cnt<tot

此时说明,绿色框中的 0 不能全部被 1 代替,必定胡留下一部分的 0

此时想要使得 t 串最大,就要让在 t 串中靠前位置的 0 变成 1

我们记录对 s 串中被用到的字符在 t 串中第一次出现的位置,按照这些位置给这些字符设定 一定的优先级

最小的交换次数为 cntcnt1

然后计算 1 的个数就使用树状数组记录即可

#include <bits/stdc++.h> using namespace std; const int N = 2e5 + 10; int p[N], tr[N], f[N]; int n, m, q; int find(int x) { if (p[x] == x) return x; return p[x] = find(p[x]); } int lowbit(int x) { return x & (-x); } void add(int pos, int x) { for (int i = pos; i <= n; i += lowbit(i)) tr[i] += x; } int query(int pos) { int ans = 0; for (int i = pos; i; i -= lowbit(i)) ans += tr[i]; return ans; } void solve() { cin >> n >> m >> q; string s; cin >> s; s = " " + s; int cnt = count(s.begin(), s.end(), '1'); for (int i = 1; i <= n + 1; i++) p[i] = i; int tot = 0; for (int i = 1; i <= m; i++) { int l, r; cin >> l >> r; for (int j = find(l); j <= r; j = find(j)) { p[j] = j + 1; f[j] = ++tot; if (s[j] == '1') add(f[j], 1); } } while (q--) { int j; cin >> j; if (f[j]) { if (s[j] == '1') { s[j] = '0'; cnt--; add(f[j], -1); } else { s[j] = '1'; cnt++; add(f[j], 1); } } else { if (s[j] == '1') { s[j] = '0'; cnt--; } else { s[j] = '1'; cnt++; } } if (cnt >= tot) cout << tot - query(tot) << endl; else cout << cnt - query(cnt) << endl; } } int main() { solve(); return 0; }

__EOF__

本文作者ljfyyds
本文链接https://www.cnblogs.com/ljfyyds/p/17605972.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   ljfyyds  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示