CF1567E Non-Decreasing Dilemma
CF1567E Non-Decreasing Dilemma
一道不难的数据结构题, 无论是思维难度还是代码难度都不大, 但是我写了半天...
原因很简单, 我改线段树写法了, 从来没这么写过, 然后就 \(gg\) 了, 没想到啊~
好了, 言归正传.
我们需要求出区间内的不下降子序列数量, 显然的是, 一个长为 \(n\) 的序列的贡献是 \(n * (n + 1) / 2\) . 因为对于一个符合要求的点对 \((p, q)\) , \(p\) 和 \(q\) 分别有 \(n\) 种选择, 但是 \((p, q)\) 合法, 当且仅当 \(p == q\) 时, \((q, p)\) 才会合法, 所以方案数为 \(n * (n + 1) / 2\) .
这样我们就用线段树来维护这么几个值:
- 最长不下降前缀
- 最长不下降后缀
- 除了最长不下降前/后缀之外的贡献
- 该段是否单调不降
然后就很简单了.
单点修改很简单, 查询的时候只需要维护当前的单调不降的序列长度以及末尾元素, 每当末尾元素大于当前段的开头元素时, 我们就将当前储存的序列长度的贡献加入答案, 然后更新当前的长度以及末尾值.
\(code:\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read() {
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
const int N = 2e5 + 5;
int n, a[N];
struct Node {
Node *ls, *rs;
ll llen, rlen, val;
int l, r, lv, rv;
bool flag;
int Mid() {
return (l + r) >> 1;
}
bool In(int L, int R) {
if (l >= L && r <= R) return 1;
return 0;
}
void Maintain() {
lv = ls->lv, rv = rs->rv;
flag = 0; val = ls->val + rs->val;
if (ls->rv <= rs->lv) {
if (ls->flag && rs->flag) llen = rlen = ls->rlen + rs->llen, flag = 1;
else if (ls->flag) llen = ls->rlen + rs->llen, rlen = rs->rlen;
else if (rs->flag) rlen = ls->rlen + rs->llen, llen = ls->llen;
else llen = ls->llen, rlen = rs->rlen, val += ((ls->rlen + rs->llen) * (ls->rlen + rs->llen + 1) >> 1);
}
else {
llen = ls->llen, rlen = rs->rlen;
if (ls->flag && !rs->flag) val += rs->llen * (rs->llen + 1) >> 1;
else if (rs->flag && !ls->flag) val += ls->rlen * (ls->rlen + 1) >> 1;
else if (!ls->flag && !rs->flag) val += (ls->rlen * (ls->rlen + 1) >> 1) + (rs->llen * (rs->llen + 1) >> 1);
}
}
} *Root = new Node;
void Build(Node* &o, int l, int r) {
o->l = l, o->r = r;
if (l == r) {
o->lv = o->rv = a[l];
o->llen = o->rlen = 1;
o->val = 0, o->flag = 1;
return;
}
int mid = o->Mid();
o->ls = new Node, o->rs = new Node;
Build(o->ls, l, mid);
Build(o->rs, mid + 1, r);
o->Maintain();
}
void Change(Node* &o, int x, int k) {
if (o->l == o->r) {
o->lv = o->rv = k;
return;
}
int mid = o->Mid();
if (x <= mid) Change(o->ls, x, k);
else if (x > mid) Change(o->rs, x, k);
o->Maintain();
}
ll ans, len;
int lst;
void Query(Node* o, int l, int r) {
if (o->In(l, r)) {
ans += o->val;
if (o->lv >= lst) {
if (o->flag) len += o->llen;
else {
len += o->llen;
ans += len * (len + 1) >> 1;
len = o->rlen;
}
}
else {
ans += len * (len + 1) >> 1;
if (o->flag) len = o->llen;
else {
len = o->llen;
ans += len * (len + 1) >> 1;
len = o->rlen;
}
}
lst = o->rv;
return;
}
int mid = o->Mid();
if (l <= mid) Query(o->ls, l, r);
if (r > mid) Query(o->rs, l, r);
}
int main() {
n = read();
int q = read();
for (int i = 1; i <= n; i++) a[i] = read();
Build(Root, 1, n);
while (q--) {
int opt = read(), x = read(), y = read();
switch (opt) {
case 1:
Change(Root, x, y);
break;
case 2:
ans = len = lst = 0;
Query(Root, x, y);
ans += len * (len + 1) >> 1;
printf("%lld\n", ans);
break;
default:
break;
}
}
return 0;
}
看不见我看不见我看不见我