专栏文章
题解:CF2101C 23 Kingdom
CF2101C题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minzvx2k
- 此快照首次捕获于
- 2025/12/02 11:04 3 个月前
- 此快照最后确认于
- 2025/12/02 11:04 3 个月前
我们注意到对于一种数,仅有最左侧的和最右侧的有贡献,故考虑选择 对位置 分别填入 种数。因为填的数越小一定不劣,所以显然填 。
答案为 ,也就是选取尽量小的 个位置作为 ,尽量大的 个位置作为 。选取时从某一侧枚举 ,填入当前最大的未填入的 填入 直到 都已经填入,小于等于 的最大未填入数字可以使用并查集维护,填入 时将其合并到 上。
如何选取 答案最大呢?令 ,我们注意到如果 ,那么我们调整 时, 和 的贡献消失,答案将会减少 ,所以调整更优;若 ,调整就不优。于是最优的 是能使得 的最大值,二分得到这个 并计算 时的下标和即可。
时间复杂度 。
Code:
CPP#include<bits/stdc++.h>
#define mem(a, v) memset(a, v, sizeof(a))
using namespace std;
const int maxn = 2e5 + 10;
int t, n;
int a[maxn];
namespace DSU{
int fa[maxn];
inline int getf(int x){
return fa[x] == x ? x : fa[x] = getf(fa[x]);
}
}
using namespace DSU;
inline pair<int, long long> calc(int x, bool op){
iota(fa, fa + x + 1, 0);
long long sum = 0;
for (int i = op ? 1 : n; op ? i <= n : i; op ? i++ : i--){
const int v = min(a[i], x), p = getf(v);
p && (fa[p] = p - 1, sum += i);
if (!getf(x)){
return make_pair(i, sum);
}
}
return make_pair(-1, -1);
}
int main(){
scanf("%d", &t);
while (t--){
scanf("%d", &n);
for (int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
int l = 0, r = n;
while (l < r){
const int mid = l + r + 1 >> 1, v1 = calc(mid, true).first, v2 = calc(mid, false).first;
if (~v1 && ~v2 && v1 < v2){
l = mid;
}else{
r = mid - 1;
}
}
printf("%lld\n", calc(l, false).second - calc(l, true).second);
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...