2025.7.21
刷题日记
好难
Codeforces Round 990 (Div. 1) B. Move Back at a Cost
https://codeforces.com/problemset/problem/2046/B
给定一个数组,你可以执行任意次操作,令a[i] = a[i] + 1,然后将a[i]挪到最后一位
你的目的是使数组最后的字典序最小
我们不难发现,只要出现了降序的地方,我们就要进行操作,直到整个数组成为升序
对此可以开一个新的空数组,如果递增就一直push_back
出现递减,就pop_back,直到最后成为递增序列
但是这样从前向后的检索并非最优解,对于第3rd样例便不可
123654的序列,应该先5后6,这样便是123467,如果先6后5,则成了123468
我们再想一下,最优的解法,一定是每个数字最多挪动一次
其实就是不符合要求的数字+1,其他数字大小不变
我们开一个结构体数组,第一位存数字,第二位存索引
然后进行按数字大小的结构体排序,接下来开始操作
如果前一个数字的原索引>后一个数字的原索引,说明这两个数字在原先的数组中是逆序,递减
这种情况我们需要将后面的数字数值+1,索引修改为INF(极大),加到数组的最后一位继续这个过程
理论上每个数字修改一次即可,但是在修改过程中,会出现改完的数字没有当前的未修改的数字大
此时反倒会继续修改改过的数字。那么该怎么解决呢?
我们可以再加一个判断,如果当前数字的索引是INF,那么不再修改,而是改前面的这个数字
这样我们就可以完成整个过程,代码如下:
点击查看代码
/*====================My_Solution====================//
执行任意次操作,令a[i] = a[i] + 1,然后挪到最后一位,使得字典序最小
那也就是说,只要出现了降序的地方,我们就要进行操作,直到整个数组成为升序
开一个新的空数组,如果递增就一直push_back
出现递减,就pop_back,直到最后成为递增序列
但是这样从前向后的检索并非最优解,对于第3rd样例便不可
123654的序列,应该先5后6,这样便是123467
如果先6后5,则成了123468
所以我们考虑对递减序列倒着去挪动?
我们再想一下,最优的解法,一定是每个数字最多挪动一次
我们进行结构体排序,然后去遍历,如果前一位的索引大于后一位
则对后一位进行挪动,这样一定为最优解
既然如此,最优解的话,一定是不符合要求的数字+1,不会更多
我们只需要进行结构体排序,当idx不再递增,便停止
//====================My_Solution====================*/
void solve () {
#define num first
#define idx second
int n;
std::cin >> n;
std::deque<pii> a;
std::vector<pii> b;
for (int i = 0; i < n; i++) {
int x;
std::cin >> x;
a.push_back({x, i});
}
std::sort(a.begin(), a.end(), [&] (pii x, pii y) {
if (x.num != y.num) return x.num < y.num;
return x.idx < y.idx;
});
while (b.size() < n) {
auto now = a.front();
a.pop_front();
b.push_back(now);
while ((b.back().num > a.front().num && a.size() && b.back().idx != inf) || (b.back().idx > a.front().idx && a.size())) {
if (b.back().num > a.front().num && a.size() && b.back().idx != inf) {
a.push_back({b.back().num + 1, inf});
b.pop_back();
}
else if (b.back().idx > a.front().idx && a.size()) {
a.push_back({a.front().num + 1, inf});
a.pop_front();
}
}
}
std::sort(b.begin(), b.end());
for (auto it : b) {
std::cout << it.num << ' ';
}
std::cout << '\n';
}
Educational Codeforces Round 175 (Rated for Div. 2) C. Limited Repainting
https://codeforces.com/problemset/problem/2070/C
现在有n个被漆成红色的板子,你可以执行最多k次操作,每次选择任意一段连续区间,将他们漆成蓝色
现给定一个字符串表示预期颜色,如果最终有哪个板子不符合预期,就会有一定的惩罚值
现在你需要设计一个方案将惩罚值最小化,并输出这个最小的惩罚值
这是一道二分答案题,我们令x为二分过程中枚举的惩罚值
在二分的内部进行数组a和字符串s的遍历(外二分内枚举,复杂度为O(nlogn))
令cnt为涂色次数,last是上一个a[i] > x的s[i]
接下来开始遍历,如果 a[i] <= x,可以直接忽略;
如果 a[i] > x 且 s[i] == 'B' 且 last != 'B',则需要进行一次涂色,cnt++
如果cnt <= k,那么就是一个合法的答案,代码如下:
点击查看代码
void solve () {
int n, k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
std::vector<int> a(n + 1);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
auto check = [&] (int mid) {
int cnt = 0;
char last = 'R';
for (int i = 0; i < n; i++) {
if (a[i] > mid) {
if (s[i] == 'B' && last != 'B') {
cnt++;
}
last = s[i];
}
}
return cnt <= k;
};
int l = 0, r = *max_element(a.begin(), a.end());
int ans = 0;
while (l <= r) {
int mid = l + r >> 1;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
std::cout << ans << '\n';
}
Educational Codeforces Round 173 (Rated for Div. 2) C. Sums on Segments
https://codeforces.com/problemset/problem/2043/C
给定一个长度为n的数组,其中只有一个数不是-1且不是1,其余的数皆是
现在要求输出这个数组所有子段和的可能值,同一个数只输出一次
这里有个结论:在一个值域为 {1,−1} 的数组里,设他的最大子段和是 min,最小子段和是 max
那么,[min, max]中的每一个整数都是答案
有了这个结论就好证了,我们可以将这个数组分为两段,那个唯一的值(记为x)的左边一半和右边一半
现在,我们可以计算左一半的最大 + 最小子段和以及后缀和,有一半的最大 + 最小字段和以及前缀和
那么,[全局最小子段和, 全局最大子段和]中的所有整数一定都在答案中
现在,我们需要考虑加上x的情况,此时我们就要用到前面计算的后缀和和前缀和了
我们需要记录左边的最大 + 最小后缀和,右边的最大 + 最小前缀和
这样以来,[x + 左边最小后缀和 + 右边最小前缀和, x + 左边最大后缀和 + 右边最大前缀和]中的每个整数就都是答案了
最后,不要忘记x本身和0也是答案,代码如下:
点击查看代码
/*====================My_Solution====================//
题解:
如果该数组仅有-1 1数字组成
那么该数组的子段和的集合包含[最小子段和, 最大子段和]中的所有数
所以我们将这个数组,以这个唯一的不为1或-1的数字为界,分为两段
对这两段各计算一次
//====================My_Solution====================*/
void solve () {
int n, pos = 0, val = 0;
std::cin >> n;
std::vector<i64> a(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
if (a[i] != 1 && a[i] != -1) {
pos = i;
val = a[i];
}
}
std::vector<i64> f1(n + 1), f2(n + 1);
std::set<i64> ans;
i64 min_l = inf, max_l = -inf;
i64 min_r = inf, max_r = -inf;
for (int i = 1; i <= pos - 1; i++) {
f1[i] = std::max(f1[i - 1] + a[i], a[i]);
f2[i] = std::min(f2[i - 1] + a[i], a[i]);
max_l = std::max(max_l, f1[i]);
min_l = std::min(min_l, f2[i]);
}
for (int i = pos + 1; i <= n; i++) {
f1[i] = std::max(f1[i - 1] + a[i], a[i]);
f2[i] = std::min(f2[i - 1] + a[i], a[i]);
max_r = std::max(max_r, f1[i]);
min_r = std::min(min_r, f2[i]);
}
for (int i = std::min(min_l, min_r); i <= std::max(max_l, max_r); i++) {
ans.insert(i);
}
std::vector<i64> pre(n + 2, 0), suf(n + 2, 0);
i64 min1 = 0, min2 = 0;
i64 max1 = 0, max2 = 0;
for (int i = pos - 1; i >= 1; i--) {
suf[i] = suf[i + 1] + a[i];
min2 = std::min(suf[i], min2);
max2 = std::max(suf[i], max2);
// debugt(suf[i], min2, max2);
}
for (int i = pos + 1; i <= n; i++) {
pre[i] = pre[i - 1] + a[i];
min1 = std::min(pre[i], min1);
max1 = std::max(pre[i], max1);
// debugt(pre[i], min1, max1);
}
for (int i = (min1 + min2); i <= (max1 + max2); i++) {
ans.insert(val + i);
}
ans.insert(0);
std::cout << ans.size() << '\n';
for (auto it : ans) {
std::cout << it << ' ';
}
std::cout << "\n";
}

浙公网安备 33010602011771号