专栏文章
题解:P12448 [COTS 2025] 观草 / Trava 题解
P12448题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mimz74s0
- 此快照首次捕获于
- 2025/12/01 17:57 3 个月前
- 此快照最后确认于
- 2025/12/01 17:57 3 个月前
考虑阶梯化贡献,即求
如果对于 ,一个极长的 满足 ,那么其对 贡献 ,其中 。我们注意到一次修改即为在 的位置将 从 变为 ,假设原本包含 的极长 区间为 ,那么将变成 和 ,使用树状数组维护区间加等差数列,单点查询即可。
我们考虑对于 ,包含 的 分别具有什么意义。我们注意到 是最大的 ,使得 , 是最大的 ,使得 ,这显然使用线段树上二分维护,考虑从根节点走到 的路径,如果某个点极深的点走的是右儿子,且其左儿子最大值大于等于 ,那么最大的 满足 ,必然在这一儿子内,在该节点内二分最大的位置即可, 的处理是类似的。时间复杂度 。
我们考虑如何计算初始的答案。设 表示最小的位置使得 , 表示最大的位置使得 ,那么对于一个 的查询和 ,其 max 能取到 当且仅当 且 ,即 ,并且能满足这一条件的 显然仅有一个,于是答案即为
考虑 变化时后面的系数取得怎样的值,不难注意到该系数为
我们发现这些项中有一些和 无关的常量,同时还有一些 ,分别开两个差分数组分别维护常量和 的系数,差分维护修改后前缀和计算答案即可。
时间复杂度 。
Code:
CPP#include<bits/stdc++.h>
#define mem(a, v) memset(a, v, sizeof(a))
using namespace std;
const int maxn = 5e5 + 10;
int n, q;
int a[maxn], pl[maxn], pr[maxn];
long long d1[maxn], d2[maxn], res[maxn];
namespace BIT{
long long tree1[maxn], tree2[maxn];
inline int lbw(int x){
return x & -x;
}
inline void add(int x, int k){
for (int i = x; i; tree1[i] += k, tree2[i] += x * k, i -= lbw(i));
}
inline long long query(int x){
long long res = 0;
for (int i = x; i <= n; res += tree2[i] - (x - 1) * tree1[i], i += lbw(i));
return res;
}
}
using namespace BIT;
namespace segtree{
struct{
int l, r, val;
} tree[maxn << 2];
inline void build(int index, int l, int r){
tree[index].l = l, tree[index].r = r;
if (l == r){
return tree[index].val = a[l], void();
}
const int mid = l + r >> 1;
build(index << 1, l, mid), build(index << 1 | 1, mid + 1, r), tree[index].val = max(tree[index << 1].val, tree[index << 1 | 1].val);
}
inline void modify(int index, int x, int k){
if (tree[index].l == tree[index].r){
return tree[index].val = k, void();
}
const int mid = tree[index].l + tree[index].r >> 1;
modify(index << 1 | x > mid, x, k), tree[index].val = max(tree[index << 1].val, tree[index << 1 | 1].val);
}
inline pair<int, int> calc(int index, int x, int k){
if (tree[index].l == tree[index].r){
return make_pair(0, 0);
}
const int mid = tree[index].l + tree[index].r >> 1;
const pair<int, int> tmp = calc(index << 1 | x > mid, x, k);
if (x <= mid){
return tree[index << 1 | 1].val >= k && !tmp.second ? make_pair(tmp.first, index << 1 | 1) : tmp;
}else{
return tree[index << 1].val >= k && !tmp.first ? make_pair(index << 1, tmp.second) : tmp;
}
}
inline int fnd1(int index, int k){
if (tree[index].l == tree[index].r){
return tree[index].l;
}
const int mid = tree[index].l + tree[index].r >> 1;
return tree[index << 1 | 1].val < k ? fnd1(index << 1, k) : fnd1(index << 1 | 1, k);
}
inline int fnd2(int index, int k){
if (tree[index].l == tree[index].r){
return tree[index].l;
}
const int mid = tree[index].l + tree[index].r >> 1;
return tree[index << 1].val < k ? fnd2(index << 1 | 1, k) : fnd2(index << 1, k);
}
}
using namespace segtree;
int main(){
scanf("%d %d", &n, &q), a[0] = a[n + 1] = 2e9;
for (int i = 1; i <= n; i++){
scanf("%d", &a[i]);
for (pl[i] = i - 1; pl[i] && a[pl[i]] < a[i]; pl[i] = pl[pl[i]]);
}
build(1, 0, n + 1);
for (int i = n; i; i--){
for (pr[i] = i + 1; pr[i] && a[pr[i]] <= a[i]; pr[i] = pr[pr[i]]);
}
for (int i = 1; i <= n; i++){
pl[i]++, pr[i]--;
long long x = i - pl[i] + 1, y = pr[i] - i + 1;
x > y && (swap(x, y), 1538), d2[1] += a[i], d2[x] -= a[i], d1[x] += a[i] * x, d1[y] += y * a[i], d1[x + y + 1] -= (x + y) * a[i], d2[y] -= a[i], d2[x + y + 1] += a[i];
}
for (int i = 1; i <= n; i++){
d1[i] += d1[i - 1], d2[i] += d2[i - 1], res[i] = d1[i] + d2[i] * i;
}
while (q--){
char op[10];
int x;
scanf("%s %d", op, &x);
if (op[0] == '?'){
printf("%lld\n", res[x] + query(x));
}else{
modify(1, x, ++a[x]);
const pair<int, int> pos = calc(1, x, a[x]);
const int l = fnd1(pos.first, a[x]) + 1, r = fnd2(pos.second, a[x]) - 1;
add(r - l + 1, 1), add(x - l, -1), add(r - x, -1);
}
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...