数据结构 Trick 之:区间子区间计数

能够解决的问题

  • \(O(n \log n)\) 可过。
  • 维护数列,无修改每次查询一个区间的所有子区间
  • 离线

思路

看到一个区间的所有子区间这种查询,直接做显然是做不了的。

考虑离线,那么将询问区间进行右端点排序,然后就可以扫描线搞掉一维。

我们从左往右枚举 \(r\) 维护线段树 \(t\) 使得 \(t_l\) 维护的是区间 \([l, r]\)。在每次将 \(r\) 向右移的时候做一次修改。

但是此时还有一个问题,这样单词只能处理一个 \(r\),多个 \(l\),那么就要请出我们的历史信息线段树,这样就 OK 了。

例题与代码

CF997E Good Subsegments - 洛谷

标准的模板。

容易发现好区间就是 \((max - min) - (r - l) = 0\) 的区间,于是直接维护即可。至于极值的修改,用一个单调栈即可。

#include <bits/stdc++.h>
using namespace std;

namespace gxk {
	void main() ;
}
int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	gxk::main();
	return 0;
}

// ---

#define int long long
#define umidd ((nodes[u].l + nodes[u].r) >> 1)
#define usz (nodes[u].r - nodes[u].l + 1)
namespace gxk {
	constexpr int maxn = 120010;
	
	int n, m, a[maxn], ans[maxn];
	struct qu {
		int l, r;
	} q[maxn];
	
	struct segnode {
		int l, r, minn, sum, hsum, tag, htag;
	} ;
	struct segtree {
		segnode nodes[maxn << 2];
		void pushup(int u) {
			nodes[u].minn = min(nodes[u << 1].minn, nodes[u << 1 | 1].minn);
			nodes[u].sum = 0;
			if (nodes[u << 1].minn == nodes[u].minn) nodes[u].sum += nodes[u << 1].sum;
			if (nodes[u << 1 | 1].minn == nodes[u].minn) nodes[u].sum += nodes[u << 1 | 1].sum;
			nodes[u].hsum = nodes[u << 1].hsum + nodes[u << 1 | 1].hsum;
			return ;
		}
		void build(int u, int l, int r) {
			nodes[u].l = l, nodes[u].r = r;
			if (usz == 1) {
				nodes[u].minn = l;
				nodes[u].sum = 1;
				return ;
			}
			build(u << 1, l, umidd);
			build(u << 1 | 1, umidd + 1, r);
			pushup(u);
			return ;
		}
		void modify(int u, int k) {
			nodes[u].minn += k;
			nodes[u].tag += k;
			return ;
		}
		void hmodify(int u, int k) {
			nodes[u].hsum += nodes[u].sum * k;
			nodes[u].htag += k;
			return ;
		}
		void pushdown(int u) {
			if (nodes[u].tag) {
				modify(u << 1, nodes[u].tag);
				modify(u << 1 | 1, nodes[u].tag);
				nodes[u].tag = 0;
			}
			if (nodes[u].htag) {
				if (nodes[u << 1].minn == nodes[u].minn) hmodify(u << 1, nodes[u].htag);
				if (nodes[u << 1 | 1].minn == nodes[u].minn) hmodify(u << 1 | 1, nodes[u].htag);
				nodes[u].htag = 0;
			}
			return ;
		}
		void update(int u, int l, int r, int k) {
			if (nodes[u].l > r || nodes[u].r < l) return ;
			if (nodes[u].l >= l && nodes[u].r <= r) {
				modify(u, k);
				return ;
			}
			pushdown(u);
			update(u << 1, l, r, k);
			update(u << 1 | 1, l, r, k);
			pushup(u);
			return ;
		}
		int query(int u, int l, int r) {
			if (nodes[u].l > r || nodes[u].r < l) return 0;
			if (nodes[u].l >= l && nodes[u].r <= r) return nodes[u].hsum;
			pushdown(u);
			return query(u << 1, l, r) + query(u << 1 | 1, l, r);
		}
	} t;
	
	struct edge {
		int l, idx;
	} ;
	vector <edge> G[maxn];
	
	stack <edge> sx, sn;
	
	void main() {
		cin >> n;
		for (int i = 1; i <= n; i++) {
			cin >> a[i];
		}
		t.build(1, 1, n);
		cin >> m;
		for (int i = 1; i <= m; i++) {
			cin >> q[i].l >> q[i].r;
			G[q[i].r].push_back({q[i].l, i});
		}
		sx.push({1145141919, 0});
		sn.push({-1145141919, 0});
		for (int i = 1, l, r, d; i <= n; i++) {
			t.modify(1, -1);
			while (sx.top().l < a[i]) {
				r = sx.top().idx;
				d = sx.top().l;
				sx.pop();
				l = sx.top().idx + 1;
//				cout << l << ' ' << r << '\n';
				t.update(1, l, r, a[i] - d);
			}
			sx.push({a[i], i});
			while (sn.top().l > a[i]) {
				r = sn.top().idx;
				d = sn.top().l;
				sn.pop();
				l = sn.top().idx + 1;
//				cout << l << ' ' << r << '\n';
				t.update(1, l, r, d - a[i]);
			}
			sn.push({a[i], i});
			t.hmodify(1, 1);
			for (edge now : G[i]) {
				ans[now.idx] = t.query(1, now.l, i);
			}
		}
		for (int i = 1; i <= m; i++) {
			cout << ans[i] << '\n';
		}
		return ;
	}
}
posted @ 2025-09-18 15:56  porse114514  阅读(5)  评论(0)    收藏  举报