P3246 [HNOI2016] 序列 题解

P3246 [HNOI2016] 序列 题解

套路地去考虑每个最小值的贡献。对于每个值 i,我们求出它左/右边第一个小于 ai 的位置 L,R,那么 l[L+1,i],r[i,R1] 会有贡献。这实际上是一个区间加的形式,同时对于询问 [L,R],也就是在 l[L,R],r[L,R] 区间内的矩形贡献。也就是需要实现一个二维区间加区间查询的形式。将贡献和查询都拆成差分的形式,将修改向右上方差分,询问向左下方差分,那么此时维护每个询问左下方的所有贡献即可。注意到不强制在线,也就是说我们可以离线下来,枚举一维,用树状数组维护另一维。考虑询问点对 (x,y),贡献点对 (i,j) 做出的贡献是 (xi+1)×(yj+1)×val,于是拆成四个树状数组维护即可。

代码:

#include <bits/stdc++.h>
#define N 100005
#define int long long
using namespace std;
int n, q;
int a[N];
int ml[N], mr[N];
int stk[N], top;

struct BIT {
	int tr[N];
	int lbt(int x) {
		return x & -x;
	}
	void add(int x, int v) {
		if (x < 1) return;
		while (x <= n) {
			tr[x] += v;
			x += lbt(x);
		}
	}
	int ask(int x) {
		if (x < 1) return 0;
		int ans = 0;
		while (x) {
			ans += tr[x];
			x -= lbt(x);
		}
		return ans;
	}
} W, XW, YW, XYW;
struct upd {
	int r, w;
};
vector<upd>updt[N];
void update(int x1, int y1, int x2, int y2, int w) {
	updt[x1].push_back({y1, w});
	updt[x1].push_back({y2 + 1, -w});
	updt[x2 + 1].push_back({y1, -w});
	updt[x2 + 1].push_back({y2 + 1, w});
}
struct que {
	int r, id, tp;
};
vector<que>qury[N];
void query(int x1, int y1, int x2, int y2, int id) {
	qury[x2].push_back({y2, id, 1});
	qury[x2].push_back({y1 - 1, id, -1});
	qury[x1 - 1].push_back({y2, id, -1});
	qury[x1 - 1].push_back({y1 - 1, id, 1});
}
int ans[N];
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> q;
	for (int i = 1; i <= n; i++) cin >> a[i];
	a[0] = -2e9;
	stk[++top] = 0;
	for (int i = 1; i <= n; i++) {
		while (top && a[stk[top]] >= a[i]) --top;
		ml[i] = stk[top];
		stk[++top] = i;
	}
	a[n + 1] = -2e9;
	top = 0;
	stk[++top] = n + 1;
	for (int i = n; i; i--) {
		while (top && a[stk[top]] > a[i]) --top;
		mr[i] = stk[top];
		stk[++top] = i;
		if (ml[i] < i && i < mr[i]) update(ml[i] + 1, i, i, mr[i] - 1, a[i]);
	}
	for (int i = 1; i <= q; i++) {
		int l, r;
		cin >> l >> r;
		query(l, l, r, r, i);
	}
	for (int i = 1; i <= n; i++) {
		for (auto j : updt[i]) {
			W.add(j.r, j.w);
			XW.add(j.r, -i * j.w);
			YW.add(j.r, -j.r * j.w);
			XYW.add(j.r, i * j.r * j.w);
		}
		for (auto j : qury[i]) {
			int sm = (i * j.r + i + j.r + 1) * W.ask(j.r) + (i + 1) * YW.ask(j.r) + (j.r + 1) * XW.ask(j.r) + XYW.ask(j.r);
			ans[j.id] += j.tp * sm;
		}
	}
	for (int i = 1; i <= q; i++) cout << ans[i] << '\n';
	return 0;
}
posted @   长安19路  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示