专栏文章
CF1849E
CF1849E题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mio4t6ap
- 此快照首次捕获于
- 2025/12/02 13:22 3 个月前
- 此快照最后确认于
- 2025/12/02 13:22 3 个月前
统计数对,可以考虑枚举右端点统计最短点,或分治统计,这里选择了枚举右端点。
令当前右端点为 ,考虑从 到 有什么变化。手玩样例,我们发现每个位置 和 贡献的变更是与其左边最近的最大或最小值影响的,分别令它们为 和 。
具体地,由于给定的是排列,因此 和 必定有一个是 。考虑到 或 为 时,对总的贡献没有任何影响,我们进行如下的分类讨论:
- 。
- 考虑到 的定义,且因为给定了排列, 一定小于 ,那么这些就会作为新增的左端点,把这一段的贡献变成 。
- 。
- 同理地, 一定大于 ,这些点一定不会成为新的左端点,把这一段的贡献变成 。
上述这一过程可以使用线段树维护, 和 可以使用单调栈维护,最后答案就是 的个数。
时间复杂度 。
CPP#include <bits/stdc++.h>
#define int long long
#define ls (p << 1)
#define rs ((p << 1) | 1)
using namespace std;
const int kN = 1e6 + 7;
int n, a[kN], stk[kN], top, mx[kN], mn[kN], ans;
int seg[kN << 2], tag[kN << 2];
void pushup(int p) {
seg[p] = seg[ls] + seg[rs];
}
void addtag(int l, int r, int p, int d) {
seg[p] = (r - l + 1) * d, tag[p] = d;
}
void pushdown(int l, int r, int p) {
if (tag[p] == -1) return;
int mid = (l + r) >> 1;
addtag(l, mid, ls, tag[p]), addtag(mid + 1, r, rs, tag[p]);
tag[p] = -1;
}
void Build(int l = 1, int r = n, int p = 1) {
if (l == r) return (void)(tag[p] = -1);
int mid = (l + r) >> 1;
Build(l, mid, ls), Build(mid + 1, r, rs), pushup(p);
}
void Update(int s, int t, int d, int l = 1, int r = n, int p = 1) {
if (s <= l && r <= t) return addtag(l, r, p, d);
int mid = (l + r) >> 1;
pushdown(l, r, p);
if (s <= mid) Update(s, t, d, l, mid, ls);
if (mid + 1 <= t) Update(s, t, d, mid + 1, r, rs);
pushup(p);
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
Build();
for (int i = 1; i <= n; i++) {
while (top && a[stk[top]] < a[i]) top--;
mx[i] = stk[top], stk[++top] = i;
}
top = 0;
for (int i = 1; i <= n; i++) {
while (top && a[stk[top]] > a[i]) top--;
mn[i] = stk[top], stk[++top] = i;
}
for (int i = 1; i <= n; i++) {
if (mn[i] < i - 1) Update(mn[i] + 1, i - 1, 0);
if (mx[i] < i - 1) Update(mx[i] + 1, i - 1, 1);
ans += seg[1];
}
cout << ans;
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...