CSP-S模拟5 D. o

由于某些原因单开一个

同步到洛谷题解

如果把每个数向前面第一个比他大的数连边,那么构成一个树形结构

可以发现对于一个 x 一直到下一个大于等于他的数之前的区间都会被他所影响,即都是他的子树,那么也就是说每个结点的子树是一个连续区间

因为是连续区间,所以他们被改变的时间也是连续的,我们考虑维护随时间变化的增量

在这个树形结构中他的父亲比他的父亲的父亲先影响到他,所以我们维护儿子与父亲的增量即可

其实上面这些废话只是告诉我们可以对每个位置得到一个四元组 (t0,l,r,d)

表示在 [l,r] 区间, 从 t0 时刻开始, 按照从左到右的位置顺序每个时刻都会在对应位置多 d 的贡献

我们把询问拆成查询两个前缀相减的形式 ansransl1 ,然后按照对时间进行扫描线,这样每个四元组的贡献大概就是下面这样

图有点丑,将就看吧

发现可以写成两个直角三角形差分的形式,如下下图

绿色减去紫色

所以四元组变成了两个三元组,就能够处理了

三元组 (t0,l,d) 表示 l 位置从 t0 时刻开始每时刻向后面有 d 的增量

考虑对一个前缀 p 进行 t 时刻的询问, 他的贡献可以写成 (min(p,l+tt0)min(p,l1))×d

这样对于 p<l 我们也不需要特殊处理,因为一减就变成 0

也就是 (t+min(pt,lt0)min(p,l1))×d

那么我们开四棵线段树分别维护

  1. l 位置维护 l×d

  2. lt0 位置维护 (lt0)×d

  3. l 位置维护 d

  4. lt0 位置维护 d

询问对 min 取的是哪个值分开讨论即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 200005;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
int n, q, a[maxn];
int sta[maxn], fa[maxn], r[maxn];
struct que{
	int p, t, id, opt;
	friend bool operator < (const que &x, const que &y){
		return x.t < y.t;
	}
}ask[maxn + maxn];
struct opt{
	int t0, l, d;
	friend bool operator < (const opt &x, const opt &y){
		return x.t0 < y.t0;
	}
}o[maxn + maxn];
ll ans[maxn];
struct tree{
	struct node{ll val, tag;}t[maxn << 3 | 1];
	void push_up(int x){
		t[x].val = t[x << 1].val + t[x << 1 | 1].val;
	}
	void modify(int x, int l, int r, int pos, ll val){
		if(l == r){
			t[x].val += val;
			return;
		}
		int mid = (l + r) >> 1;
		if(pos <= mid)modify(x << 1, l, mid, pos, val);
		else modify(x << 1 | 1, mid + 1, r, pos, val);
		push_up(x);
	}
	ll query(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x].val;
		int mid = (l + r) >> 1; ll ans = 0;
		if(L <= mid)ans += query(x << 1, l,  mid, L, R);
		if(R > mid)ans += query(x << 1 | 1, mid + 1, r, L, R);
		return ans;
	}
}t[4];
ll sum[maxn];
int main(){
	n = read(), q = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)sum[i] = sum[i - 1] + a[i];
	for(int i = 1; i <= q; ++i){
		int t = read(), l = read(), r = read();
		ask[i + i - 1].id = ask[i + i].id = i;
		ask[i + i - 1].t = ask[i + i].t = t;
		ask[i + i - 1].p = l - 1, ask[i + i - 1].opt = -1;
		ask[i + i].p = r, ask[i + i].opt = 1;
	}
	int top = 0;
	for(int i = 1; i <= n; ++i){
		while(top && a[sta[top]] < a[i])r[sta[top]] = i, --top;
		fa[i] = sta[top]; sta[++top] = i;
	}
	while(top)r[sta[top--]] = n + 1;
	sort(ask + 1, ask + q + q + 1);
	int cnt = 0;
	for(int i = 1; i <= n; ++i)if(fa[i]){
		o[++cnt].d = a[fa[i]] - a[i];
		o[cnt].l = i; o[cnt].t0 = i - fa[i];
		if(r[i]){
			o[++cnt].d = a[i] - a[fa[i]];
			o[cnt].l = r[i]; o[cnt].t0 = r[i] - fa[i];
		} 
	}
	sort(o + 1, o + cnt + 1);
	int op = 1;
	for(int i = 1; i <= q + q; ++i){
		while(op <= cnt && o[op].t0 <= ask[i].t){
			t[0].modify(1, 0, n, o[op].l - 1, 1ll * (o[op].l - 1) * o[op].d);
			t[1].modify(1, 0, n + n, o[op].l - o[op].t0 + n, 1ll * (o[op].l - o[op].t0) * o[op].d);
			t[2].modify(1, 0, n, o[op].l - 1, o[op].d);
			t[3].modify(1, 0, n + n, o[op].l - o[op].t0 + n, o[op].d);
			++op;
		}
		ll nans = 0;
		if(ask[i].p == 0)nans = 0;
		else{
			nans = ask[i].t * t[2].query(1, 0, n, 1, n);
			nans = nans + t[1].query(1, 0, n + n, 1, ask[i].p - ask[i].t + n);
			nans = nans + t[3].query(1, 0, n + n, ask[i].p - ask[i].t + n + 1, n + n) * (0ll + ask[i].p - ask[i].t);
			nans = nans - t[0].query(1, 0, n, 1, ask[i].p);
			nans = nans - t[2].query(1, 0, n, ask[i].p + 1, n) * 1ll * ask[i].p;
			nans = nans + sum[ask[i].p];
		}
		ans[ask[i].id] += nans * ask[i].opt;
	}
	for(int i = 1; i <= q; ++i)printf("%lld\n", ans[i]);
	return 0;
}
posted @   Chen_jr  阅读(51)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示