• 题解 CF997E 【Good Subsegments】


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

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

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

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

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

    细节看代码吧。

    (Code:)

    #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
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    毕业设计过程复盘
    关于理想
    Python之网络模型与图形绘制工具networkx
    Python之Numpy:二元函数绘制/三维数据可视化/3D
    JavaScript之参数传递方式
    Python之滑动窗口
    [转] JavaScript 原型理解与创建对象应用
    [转] JavaScript 和事件
    [转] 三步将你的 React Native 项目运行在 Web 浏览器上面
    [转] Webpack 入门指迷
  • 原文地址:https://www.cnblogs.com/JiaZP/p/13381983.html
Copyright © 2020-2023  润新知