题解:P15133 [ROIR 2026] 最后的滑动窗口问题

题意

给定长度为 \(n\) 的序列 \(a\)\(q\) 次询问 \(l,r,k\),求 \(\sum\limits_{i=l}^{r-k+1}\min\limits_{j=i}^{i+k-1}a_j\)\(n,q\leq 10^5\)

题解

首先用差分转化成前缀询问。

考虑经典技巧,把 \(\min\limits_{j=i}^{i+k-1}a_j\) 转化为 \(\sum\limits_{v\geq 1}\left[\min\limits_{j=i}^{i+k-1}a_j\geq v\right]\)。令 \(b_{v,i}=[a_i\geq v]\),则问题转化成求:

\[\sum\limits_{v\geq 1}\sum\limits_{i=l}^{r-k+1}\left[\sum\limits_{j=i}^{i+k-1}b_{v,j}=k\right] \]

固定 \(v\),考察 \(b_v\) 中的每个极长连续 \(1\)\([L,R]\),则 \([L,R]\) 对询问 \((1,r,k)\) 的贡献为 \(\max(len-k+1,0)\),其中 \(len\)\([L,R]\)\([1,r]\) 的交集长度。

进一步地,我们发现 \(b\) 中只有 \(\mathcal{O}(n)\) 种本质不同的极长连续 \(1\) 段。具体来说,对于每个数 \(a_i\),求出其作为最小值的极长区间 \([L_i,R_i]\),则当 \(v\geq a_i>\max(a_{L_i-1},a_{R_i+1})\) 时,\([L_i,R_i]\) 始终是 \(b\) 中的极长连续 \(1\) 段。

现在问题转化成:给你 \(n\) 个区间 \([L_i,R_i]\)\(t_i\)\(q\) 次询问 \((r,k)\),一个区间对一个询问的贡献为 \(\max(len-k+1,0)\times t_i\),求贡献总和。

不妨分类讨论 \([L_i,R_i]\)\([1,r]\) 的位置关系。

\(R_i\leq r\),则 \(len=R_i-L_i+1\)。贡献条件为 \(R_i\leq r\land R_i-L_i+1\geq k\),贡献值为 \(R_i-L_i+1-k\)

\(L_i\leq r<R_i\),则 \(len=r-L_i+1\)。贡献条件为 \(R_i>r\land L_i\leq r-k+1\),贡献值为 \(r-L_i+1-k\)

两者都是二维偏序的形式,容易用树状数组维护。

时间复杂度为 \(\mathcal{O}(n\log{n})\)

主要代码
int n, q, a[N];
ll ans[N];
int top, stk[N];
struct Op { int l, r, t; } op[N];
int sz_qr;
struct Query { int id, tp, r, k; } qr[N << 1];

struct BIT {
	ll c[N];
	void init() { fill(c + 1, c + n + 1, 0); }
	ll query(int x) {
		ll res = 0;
		while (x) res += c[x], x -= lowbit(x);
		return res;
	}
	void add(int x, ll v) { while (x <= n) c[x] += v, x += lowbit(x); }
} ft1, ft2;

int main() {
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> q;
	for (int i = 1; i <= n; ++i) cin >> a[i];
	for (int i = 1; i <= n; ++i) {
		while (top && a[stk[top]] > a[i]) op[stk[top--]].r = i - 1;
		op[i].l = stk[top] + 1, stk[++top] = i;
	}
	while (top) op[stk[top--]].r = n;
	for (int i = 1; i <= n; ++i) op[i].t = a[i] - max(a[op[i].l - 1], a[op[i].r + 1]);
	for (int i = 1, l, r, k; i <= q; ++i) {
		cin >> l >> r >> k;
		if (l + k - 2 >= k) qr[++sz_qr] = {i, -1, l + k - 2, k};
		qr[++sz_qr] = {i, 1, r, k};
	}
	sort(op + 1, op + n + 1, [](const Op &lhs, const Op &rhs) { return lhs.r - lhs.l > rhs.r - rhs.l; });
	sort(qr + 1, qr + sz_qr + 1, [](const Query &lhs, const Query &rhs) { return lhs.k > rhs.k; });
	for (int i = 1, j = 1; i <= sz_qr; ++i) {
		auto [id, tp, r, k] = qr[i];
		while (j <= n && op[j].r - op[j].l + 1 >= k) {
			ft1.add(op[j].r, op[j].t);
			ft2.add(op[j].r, (ll)op[j].t * (op[j].r - op[j].l + 1));
			++j;
		}
		ans[id] += (ft2.query(r) - (k - 1) * ft1.query(r)) * tp;
	}
	ft1.init(), ft2.init();
	sort(op + 1, op + n + 1, [](const Op &lhs, const Op &rhs) { return lhs.r > rhs.r; });
	sort(qr + 1, qr + sz_qr + 1, [](const Query &lhs, const Query &rhs) { return lhs.r > rhs.r; });
	for (int i = 1, j = 1; i <= sz_qr; ++i) {
		auto [id, tp, r, k] = qr[i];
		while (j <= n && op[j].r > r) {
			ft1.add(op[j].l, op[j].t);
			ft2.add(op[j].l, (ll)op[j].t * op[j].l);
			++j;
		}
		ans[id] += ((r - k + 2) * ft1.query(r - k + 1) - ft2.query(r - k + 1)) * tp;
	}
	for (int i = 1; i <= q; ++i) cout << ans[i] << '\n';
	return 0;
}
posted @ 2026-02-09 14:21  P2441M  阅读(12)  评论(0)    收藏  举报