2023.08.29T3 - summer - solution
summer
Problem
有一条长为 \(n\) 的道路,每个单位上有一只虫,将虫从左到右依次编号为 \(1, 2, \dots, n\),第 \(i\) 只虫的狡猾度为 \(a_i\)。
一个人带着一个捕虫网,要将这 \(n\) 只虫全部捕完。如果他从 \(x\) 出发,则捕虫网的能力值 \(v\) 初始化为 \(a_x\),并假设他已经将 \(x\) 处的虫捕捉。之后他向两边开始捕虫。若该人已经捕完了 \([l, r]\) 内的所有虫,且当前捕虫网的能力值为 \(v\),则他可以进行以下操作:
- 若 \(l \neq 1\) 且 \(a_{l - 1} \le v\),那么他可以花费一精力捕捉 \(l - 1\) 处的虫。
- 若 \(r \neq n\) 且 \(a_{r + 1} \le v\),那么他可以花费一精力捕捉 \(r + 1\) 处的虫。
- 花费 \(k\) 精力使 \(v = \min(a_{l - 1}, a_{r + 1})\)。这里定义 \(a_0 = a_{n + 1} = \infty\)。
多次修改/询问 \(x, l, r\):交换 \(a_{x}, a_{x + 1}\),然后查询从 \(\forall i, l \le i \le r\) 出发需花费的精力值的和之和。
\(1 \le n, Q \le 10^5, 1 \le a_i \le 10^9\)。
Solution
挺好的题,题解也写得很清楚,因此我不过是把题解抄一遍。
赛时打了 \(40\) 分,然后挂了 \(20\) 分,因为不会前缀和(这个人暴力求区间和,铸币吧)。
前 \(40\) 分就是记忆化搜索 + 单调栈:
首先考察对于一个确定的序列,如何求出一段区间的权值和。那么首先就要知道如何快速计算一个点的权值和。
权值由两部分组成:
加入新点,这部分权值是固定的,必为 \(n - 1\)。
改变捕虫网能力值,贪心地考虑这个能力值只增不减,很快得到以下做法:
设 \(f_x\) 表示在 \(x\) 起始时至少要更改几次捕虫网的能力值。
对于 \(f_x\),求出左侧最大的 \(lp_x\) 使得 \(a_{lp_x} > a_x\)(若没有则记 \(lp_x = 0\)),求出右侧最小的 \(rp_x\) 使得 \(a_{rp_x} > a_x\)(若没有则记 \(rp_x = n + 1\))。为方便表示,记 \(l = lp_x, r = rp_x\)。
- \(a_l < a_r\) 时:\(f_x = f_l + 1\)
- \(a_l > a_r\) 时:\(f_x = f_r + 1\)
- \(a_l = a_r\) 时:\(f_x = f_l + 1 = f_r + 1\)
初始值:\(f_0 = f_{n + 1} = -1\)。
可以通过记忆化搜索对确定的序列 \(O(n)\) 求出 \(f\)。
但是题解对于这个做法有一个更加形式化的描述,我认为很不错:
设 \(L_x\) 表示 \(a_1, a_2, \dots, a_x\) 的后缀最大值集合,\(R_x\) 表示 \(a_x, a_{x + 1}, \dots, a_n\) 的前缀最大值集合,那么 \(f_x = |L_x \cup R_x| - 1\)。(减 \(1\) 是要减去 \(a_x\) 本身。)
这个说法使得之后想正解更加清晰。
只交换相邻两个数,想到了维护变化量,也想到了这道题可能要用数据结构维护,但是赛时把暴力打完就跑了(四道题里面分打得最多的一道)
交换 \(a_x, a_{x + 1}\):
-
若 \(a_x = a_{x + 1}\):显然没有影响。
-
若 \(a_x < a_{x + 1}\):
-
对于 \(i < x\):显然对 \(L_i\) 没有影响,于是只考虑 \(R_i\)。
交换后,影响只有一种可能:将 \(a_x\) 在某些 \(R_i\) 中删除。
\(R_i\) 被影响到 \(\iff\) \(\max\limits_{j = i}^{x - 1}a_j < a_x\)。(注意不要取等!)
\(\max\limits_{j = i}^{x - 1}a_j\) 随 \(i\) 变大而减小,所以被影响到的区间可以通过二分求出。由于 \(a\) 数组是在变化的,因此需要用数据结构维护,可以用线段树二分求出被影响的区间。
假设已经求得被影响的区间是 \([l, r]\)(显然 \(r = x - 1\),之后的区间同理,只有一个端点的求解需要线段树上二分),那么怎么执行影响呢?维护集合太难了,考虑直接维护 \(f_i\)。但还要注意 \(L_i\) 对 \(f_i\) 的影响,这里又用到了一步简单而巧妙地判断:
- 若 \(a_{l - 1} = a_x\),则 \(\forall i \in [l, r], a_{x} = a_{l - 1} \in L_{i}\),\(f\) 不变。
- 否则,因为 \(a_{l - 1} \ge a_x\)(根据被影响区间的定义),所以必然有 \(a_{l - 1} > a_x\),而 \([l, r]\) 内不存在 \(a_x\),所以 \(\forall i \in [l, r], a_{l - 1} \in L_i, a_x \not\in L_i\),区间 \([l, r]\) 的 \(f\) 值减 \(1\)。
-
对于 \(i > x + 1\):同理,对 \(R_i\) 没有影响,但是对 \(L_i\) 有影响。
交换带来的影响为,将 \(a_x\) 加入某些 \(L_i\)。
\(L_i\) 被影响到 \(\iff\) \(\max\limits_{j = x + 1}^{i}a_j < a_x\)。
类似地处理即可。
-
对于 \(i = x\) 和 \(i = x + 1\):如果用 \(f_x = |L_x \cup R_x| - 1\) 的定义,似乎不太好操作,可以考虑通过原始的求法更新 \(f_x, f_{x + 1}\)。
现在问题在于如何快速得到 \(lp_x, rp_x, lp_{x + 1}, rp_{x + 1}\)。这不还是线段树二分?
完结撒花。但还是把 \(a_x > a_{x + 1}\) 的情况写一下。
-
-
若 \(a_x > a_{x + 1}\):
-
对于 \(i < x\):对 \(L_i\) 无影响,对 \(R_i\) 的影响为:将 \(a_{x + 1}\) 插入某些 \(L_i\)。
\(L_i\) 被影响到 \(\iff\) \(\max\limits_{j = i}^{x - 1}a_j < a_{x + 1}\)。
-
对于 \(i < x + 1\):对 \(R_i\) 无影响,对 \(L_i\) 的影响为:将 \(a_{x + 1}\) 在某些 \(R_i\) 中删除。
\(R_i\) 被影响到 \(\iff\) \(\max\limits_{j = x + 1}^{i}a_j < a_{x + 1}\)。
-
对于 \(i = x\) 和 \(i = x + 1\):按原始方法处理。
-
一些细节:
- 暴力更新 \(f_x, f_{x + 1}\) 时,要先更新(交换后) \(a\) 值大的。
- 处理 \(i < x\) 时,被影响区间的右端点为 \(x - 1\)。处理 \(i > x + 1\) 时,被影响区间右端点为 \(x + 2\)。笔者习惯性地打成 \(x + 1\) 了。
- 我找被影响区间时是转成找 \(\ge a_x(a_{x + 1})\) 的左侧最大/右侧最小位置。
- 只有 与 \(f\) 无关 的 询问 才可以不用 pushdown(对于修改,即使与 \(f\) 无关,但由于存在 pushup,仍然会使区间信息出错)!(拜谢 \(\color{black}{b} \color{red}{ottyl}\))
#include<bits/stdc++.h>
#define LL long long
#define DB double
#define MOD 1000000007
#define ls(x) x << 1
#define rs(x) x << 1 | 1
#define lowbit(x) x & (-x)
#define PII pair<int, int>
#define MP make_pair
#define VI vector<int>
#define VII vector<int>::iterator
#define all(x) x.begin(), x.end()
#define EB emplace_back
#define SI set<int>
#define SII set<int>::iterator
#define QI queue<int>
using namespace std;
template<typename T> void chkmn(T &a, const T &b) { (a > b) && (a = b); }
template<typename T> void chkmx(T &a, const T &b) { (a < b) && (a = b); }
int inc(const int &a, const int &b) { return a + b >= MOD ? a + b - MOD : a + b; }
int dec(const int &a, const int &b) { return a - b < 0 ? a - b + MOD : a - b; }
int mul(const int &a, const int &b) { return 1LL * a * b % MOD; }
int sqr(const int &a) { return 1LL * a * a % MOD; }
void Inc(int &a, const int &b) { ((a += b) >= MOD) && (a -= MOD); }
void Dec(int &a, const int &b) { ((a -= b) < 0) && (a += MOD); }
void Mul(int &a, const int &b) { a = 1LL * a * b % MOD; }
void Sqr(int &a) { a = 1LL * a * a % MOD; }
int qwqmi(int x, int k = MOD - 2)
{
int res = 1;
while(k)
{
if(k & 1) Mul(res, x);
Sqr(x), k >>= 1;
}
return res;
}
const int N = 1e5 + 5;
const int INF = 1e9 + 7;
int n, k, Q, a[N];
int f[N], lp[N], rp[N], stk[N], top;
int dfs(int x)
{
if(x == 0 || x == n + 1)
return -1;
if(f[x] != -1)
return f[x];
int l = lp[x];
int r = rp[x];
if(a[l] < a[r])
return (f[x] = dfs(l) + 1);
else if(a[l] > a[r])
return (f[x] = dfs(r) + 1);
else return (f[x] = max(dfs(l), dfs(r)) + 1);
}
struct SGT
{
struct SegTree
{
int l, r;
int maxa;
LL sumf;
LL add;
}tr[N << 4];
void pushup(int p)
{
tr[p].maxa = max(tr[ls(p)].maxa, tr[rs(p)].maxa);
tr[p].sumf = tr[ls(p)].sumf + tr[rs(p)].sumf;
}
void build(int p, int l, int r)
{
tr[p].l = l;
tr[p].r = r;
tr[p].add = 0;
if(l == r)
{
tr[p].maxa = a[l];
tr[p].sumf = f[l];
return;
}
int mid = l + (r - l) / 2;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
pushup(p);
}
void cal(SegTree &u, LL v)
{
u.add += v;
u.sumf += 1LL * (u.r - u.l + 1) * v;
}
void pushdown(int p)
{
if(tr[p].add)
{
cal(tr[ls(p)], tr[p].add);
cal(tr[rs(p)], tr[p].add);
tr[p].add = 0;
}
}
void modify_a(int p, int x, int v)
{
if(tr[p].l == tr[p].r)
{
tr[p].maxa = v;
return;
}
pushdown(p);
int mid = tr[p].l + (tr[p].r - tr[p].l) / 2;
if(mid >= x) modify_a(ls(p), x, v);
else modify_a(rs(p), x, v);
pushup(p);
}
void modify_f(int p, int x, int v)
{
if(tr[p].l == tr[p].r)
{
cal(tr[p], v);
return;
}
pushdown(p);
int mid = tr[p].l + (tr[p].r - tr[p].l) / 2;
if(mid >= x) modify_f(ls(p), x, v);
else modify_f(rs(p), x, v);
pushup(p);
}
void modify_f(int p, int l, int r, int v)
{
if(tr[p].l >= l && tr[p].r <= r)
{
cal(tr[p], v);
return;
}
pushdown(p);
int mid = tr[p].l + (tr[p].r - tr[p].l) / 2;
if(mid >= l) modify_f(ls(p), l, r, v);
if(mid < r) modify_f(rs(p), l, r, v);
pushup(p);
}
int query_f(int p, int x)
{
if(tr[p].l == tr[p].r)
return tr[p].sumf;
pushdown(p);
int mid = tr[p].l + (tr[p].r - tr[p].l) / 2;
if(mid >= x) return query_f(ls(p), x);
else return query_f(rs(p), x);
}
LL query_sumf(int p, int l, int r)
{
if(tr[p].l >= l && tr[p].r <= r)
return tr[p].sumf;
pushdown(p);
LL res = 0;
int mid = tr[p].l + (tr[p].r - tr[p].l) / 2;
if(mid >= l) res += query_sumf(ls(p), l, r);
if(mid < r) res += query_sumf(rs(p), l, r);
return res;
}
int query_l(int p, int x, int v)
{
if(tr[p].maxa < v || tr[p].l >= x)
return 0;
if(tr[p].l == tr[p].r) return tr[p].l;
int res = query_l(rs(p), x, v);
if(res != 0) return res;
return query_l(ls(p), x, v);
}
int query_r(int p, int x, int v)
{
if(tr[p].maxa < v || tr[p].r <= x)
return n + 1;
if(tr[p].l == tr[p].r) return tr[p].l;
int res = query_r(ls(p), x, v);
if(res != n + 1) return res;
return query_r(rs(p), x, v);
}
}T;
void update_f(int x) // update f[x] / f[x + 1] brutely
{
int w = T.query_f(1, x), _w; // the old / new
int l = T.query_l(1, x, a[x] + 1);
int r = T.query_r(1, x, a[x] + 1);
if(a[l] == INF && a[r] == INF)
return void(T.modify_f(1, x, -w));
if(a[l] < a[r])
_w = T.query_f(1, l) + 1;
else
_w = T.query_f(1, r) + 1;
T.modify_f(1, x, _w - w);
}
void preprocess()
{
a[0] = a[n + 1] = INF;
for(int i = 0; i <= n + 1; ++i)
f[i] = -1;
stk[top = 0] = 0;
for(int i = 1; i <= n; ++i)
{
while(top && a[stk[top]] <= a[i]) --top;
lp[i] = stk[top];
stk[++top] = i;
}
stk[top = 0] = n + 1;
for(int i = n; i >= 1; --i)
{
while(top && a[stk[top]] <= a[i]) --top;
rp[i] = stk[top];
stk[++top] = i;
}
for(int i = 1; i <= n; ++i)
dfs(i);
T.build(1, 1, n);
}
int main()
{
freopen("summer.in", "r", stdin);
freopen("summer.out", "w", stdout);
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
preprocess();
scanf("%d", &Q);
for(int cas = 1; cas <= Q; ++cas)
{
int x, ql, qr;
scanf("%d %d %d", &x, &ql, &qr);
int l, r;
if(a[x] < a[x + 1])
{
l = T.query_l(1, x, a[x]), ++l; r = x - 1;
if(a[l - 1] > a[x] && l <= r)
T.modify_f(1, l, r, -1);
r = T.query_r(1, x + 1, a[x]), --r; l = x + 2;
if(a[r + 1] > a[x] && l <= r)
T.modify_f(1, l, r, 1);
}
else if(a[x] > a[x + 1])
{
l = T.query_l(1, x, a[x + 1]), ++l; r = x - 1;
if(a[l - 1] > a[x + 1] && l <= r)
T.modify_f(1, l, r, 1);
r = T.query_r(1, x + 1, a[x + 1]), --r; l = x + 2;
if(a[r + 1] > a[x + 1] && l <= r)
T.modify_f(1, l, r, -1);
}
T.modify_a(1, x, a[x + 1]);
T.modify_a(1, x + 1, a[x]);
swap(a[x], a[x + 1]);
if(a[x] > a[x + 1]) update_f(x), update_f(x + 1);
if(a[x] < a[x + 1]) update_f(x + 1), update_f(x);
LL res = T.query_sumf(1, ql, qr);
res = 1LL * res * k;
res += 1LL * (qr - ql + 1) * (n - 1);
printf("%lld\n", res);
}
return 0;
}