题解 CF997E 【Good Subsegments】

可以先做一下弱化版:CF526F Pudding Monsters,那道题是本题的基础。

由于这是个排列,因此好区间可以转化为满足 maxmin=rl 的区间。其中 max,min 分别表示区间最大值和最小值,l,r 分别表示区间左右端点。我们可以枚举 r,那么限制变为 maxmin+l=r。又因为对于所有区间,都有 maxmin>=rl,所以好区间可以转化为“以 r 为右端点,且 maxmin+l 最小左端点的个数”。

我们从左往右扫一遍,用单调栈和线段树维护最值,扫的时候顺便统计一下全局最小值个数,这就是前面那道题的做法。

对于这道题,我们可以离线,将询问挂到右端点,“子区间”转化为“前缀的后缀”。如果只考虑一个前缀的所有后缀的答案,这题只不过多限制左端点的范围,不能小于 L,这个好说,查全局最小值个数改为查区间最小值个数即可。再考虑所有前缀的贡献,这个可以直接在线段树上打“历史贡献”的标记,查询就把节点的“历史贡献”加和即可。

总结一下,我们需要一棵线段树,支持区间加,单点修改,区间查历史贡献。还需要俩单调栈,维护最大最小值,并在线段树上进行操作。

细节看代码吧。

Code:

Copy
#define N 201000 #define NN 801000 #define int long long template <typename T> inline void read(T &x) { x = 0; char c = getchar(); bool flag = false; while (!isdigit(c)) { if (c == '-') flag = true; c = getchar(); } while (isdigit(c)) { x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } if (flag) x = -x; } using namespace std; const int inf = 987654321; int n; int h[N]; struct edge{ int nxt, to, id; }e[N]; int head[N], ecnt; inline void addedge(int from, int to, int id) {//邻接表挂询问 e[++ecnt] = (edge){head[from], to, id}; head[from] = ecnt; } int ans[N]; struct node { int mn, cnt; node(int mnn = inf, int cntt = 0) { mn = mnn, cnt = cntt; } node operator +(const node a) const { return node(min(mn, a.mn), mn == a.mn ? cnt + a.cnt : (mn < a.mn ? cnt : a.cnt)); } }nd[NN]; int ls[NN], rs[NN], atag[NN], ctag[NN], res[NN], root, ttot; void build(int L, int R, int &cur) { cur = ++ttot; if (L == R) return ; int mid = (L + R) >> 1; build(L, mid, ls[cur]); build(mid + 1, R, rs[cur]); } inline void pushup(int cur) { nd[cur] = nd[ls[cur]] + nd[rs[cur]]; } inline void pusha(int cur, int v) {//打加法标记 if (!cur) return ; nd[cur].mn += v; atag[cur] += v; } inline void pushc(int cur, int mn, int c) {//打历史标记 if (!cur || nd[cur].mn != mn) return ; //只有儿子最小值和父亲相同时才能继承贡献 res[cur] += nd[cur].cnt * c; ctag[cur] += c; } inline void pushdown(int cur) {//下放标记。注意顺序 if (atag[cur]) pusha(ls[cur], atag[cur]), pusha(rs[cur], atag[cur]), atag[cur] = 0; if (ctag[cur]) pushc(ls[cur], nd[cur].mn, ctag[cur]), pushc(rs[cur], nd[cur].mn, ctag[cur]), ctag[cur] = 0; } void modify(int L, int R, int l, int r, int v, int cur) {//区间加 if (l <= L && R <= r) { pusha(cur, v); return ; } pushdown(cur); int mid = (L + R) >> 1; if (l <= mid) modify(L, mid, l, r, v, ls[cur]); if (r > mid) modify(mid + 1, R, l, r, v, rs[cur]); pushup(cur); } void modify(int L, int R, int pos, int v, int cur) {//单点修改 if (L == R) return nd[cur] = (node){v, 1}, void(); pushdown(cur); int mid = (L + R) >> 1; if (pos <= mid) modify(L, mid, pos, v, ls[cur]); else modify(mid + 1, R, pos, v, rs[cur]); pushup(cur); } int query(int L, int R, int l, int r, int cur) { if (l <= L && R <= r) return res[cur]; pushdown(cur); int mid = (L + R) >> 1, tmp = 0; if (l <= mid) tmp = query(L, mid, l, r, ls[cur]); if (r > mid) tmp += query(mid + 1, R, l, r, rs[cur]); return tmp; } struct Seg {//单调栈 int l, r, v;//l ~ r 的最值都是 v Seg(int ll = 0, int rr = 0, int vv = 0) { l = ll, r = rr, v = vv; } bool operator <(const Seg a) const { return v < a.v; } bool operator >(const Seg a) const { return v > a.v; } }smx[N], smn[N]; int mxtop, mntop; signed main() { read(n); for (register int i = 1; i <= n; ++i) read(h[i]); int q; read(q); for (register int i = 1; i <= q; ++i) { int l, r; read(l), read(r); addedge(r, l, i); } build(1, n, root); for (register int i = 1; i <= n; ++i) { //维护最值 Seg s(i, i, h[i]); while (mxtop && s > smx[mxtop]) modify(1, n, smx[mxtop].l, smx[mxtop].r, h[i] - smx[mxtop].v, root), s.l = smx[mxtop].l, --mxtop; smx[++mxtop] = s; s = Seg(i, i, h[i]); while (mntop && s < smn[mntop]) modify(1, n, smn[mntop].l, smn[mntop].r, -h[i] + smn[mntop].v, root), s.l = smn[mntop].l, --mntop; smn[++mntop] = s; modify(1, n, i, i, root);//插入新点 pushc(root, i, 1);//将当前的合法区间计入贡献 for (register int j = head[i]; j; j = e[j].nxt) { int l = e[j].to, id = e[j].id; ans[id] = query(1, n, l, i, root); } } for (register int i = 1; i <= q; ++i) printf("%lld\n", ans[i]); return 0; }
posted @   JiaZP  阅读(180)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示