CSP-S模拟5 D. o

由于某些原因单开一个

同步到洛谷题解

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

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

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

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

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

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

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

图有点丑,将就看吧

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

绿色减去紫色

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

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

考虑对一个前缀 \(p\) 进行 \(t\) 时刻的询问, 他的贡献可以写成 \((min(p, l + t - t_0) - min(p, l - 1)) \times d\)

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

也就是 \((t + min(p - t, l - t_0) - min(p, l - 1)) \times d\)

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

  1. \(l\) 位置维护 \(l \times d\)

  2. \(l - t_0\) 位置维护 \((l - t_0) \times d\)

  3. \(l\) 位置维护 \(d\)

  4. \(l - t_0\) 位置维护 \(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 @ 2022-09-16 16:30  Chen_jr  阅读(51)  评论(4编辑  收藏  举报