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;
}
posted @ 2023-08-29 17:14  Schucking_Sattin  阅读(21)  评论(0编辑  收藏  举报