洛谷传送门

LOJ 传送门

考虑若原来的序列是不降的,那么进行 \(1\) 操作或 \(2\) 操作序列仍然不降。那么 \(1\) 操作直接线段树上二分然后打覆盖标记,\(2\) 操作直接打标记即可。

考虑一般情况,发现某个时刻所有被 \(1\) 操作影响过的 \(i\)(存在一次 \(1\) 操作使得 \(a_i \ge v\))组成的集合 \(S\)\(S\) 内部的序列是单调的。

于是整个序列被分成了两部分:\(S\)\([1, n] \setminus S\)。对于 \(S\)\(1\) 操作直接线段树上二分然后打标记。\(2\) 操作就分别对两个部分打个标记即可。

线段树一个结点需要维护:\(S\) 部分的最大值及其最大下标(为了 \(2\) 操作后计算新的最大值),两部分分别的和及其下标的和,\(S\) 部分的元素个数和这个区间在 \(S\) 部分的最大下标(打覆盖标记时需要将最大值的最大下标重置为这个值)。

剩下最后一个问题:如何计算一个元素何时加入 \(S\)。考虑二分 \(mid\),相当于查询是否 \(\exists j \in [1, mid], b_j \times i + a_i \ge v_j\)(其中 \(b_i\) 为第 \(i\)\(1\) 操作前面的 \(2\) 操作次数)。变形得 \(\min\limits_{j = 1}^{mid} \{ -b_j \times i + v_j \} \le a_i\)。考虑整体二分,维护一个 \((b_j, v_j)\) 的下凸包,check 就维护一个凸包上的指针即可。因为 \(b_j\) 单调,所以不用排序。注意可能存在若干个 \(b_j\) 相等,这种取它们 \(v_j\) 的最小值即可。

总时间复杂度 \(O(n \log n)\)

code

// Problem: #3673. 「北大集训 2021」简单数据结构
// Contest: LibreOJ
// URL: https://loj.ac/p/3673
// Memory Limit: 512 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 200100;
const ll inf = 0x3f3f3f3f3f3f3f3fLL;

ll n, m, a[maxn], tot, f[maxn], id[maxn];
vector<int> vc[maxn];

struct pig {
	ll op, x, y;
} qq[maxn];

struct wwh {
	ll x, y;
	wwh(ll a = 0, ll b = 0) : x(a), y(b) {}
} p[maxn], stk[maxn];

inline wwh operator + (const wwh &a, const wwh &b) {
	return wwh(a.x + b.x, a.y + b.y);
}

inline wwh operator - (const wwh &a, const wwh &b) {
	return wwh(a.x - b.x, a.y - b.y);
}

inline ll operator * (const wwh &a, const wwh &b) {
	return a.x * b.y - a.y * b.x;
}

void dfs(int l, int r, vector<int> Q) {
	if (l > r || Q.empty()) {
		return;
	}
	if (l == r) {
		for (int i : Q) {
			f[i] = (p[l].y - i * p[l].x <= a[i] ? l : 0);
		}
		return;
	}
	int mid = (l + r) >> 1, top = 0;
	for (int i = l, j = l; i <= mid; i = (++j)) {
		while (j < mid && p[j + 1].x == p[i].x) {
			++j;
		}
		ll mn = 9e18;
		for (int k = i; k <= j; ++k) {
			mn = min(mn, p[k].y);
		}
		wwh u(p[i].x, mn);
		while (top >= 2 && (stk[top] - stk[top - 1]) * (u - stk[top - 1]) <= 0) {
			--top;
		}
		stk[++top] = u;
	}
	vector<int> L, R;
	int j = 1;
	for (int i : Q) {
		while (j < top && (stk[j + 1].y - stk[j].y) <= i * (stk[j + 1].x - stk[j].x)) {
			++j;
		}
		if (stk[j].y - i * stk[j].x <= a[i]) {
			L.pb(i);
		} else {
			R.pb(i);
		}
	}
	dfs(l, mid, L);
	dfs(mid + 1, r, R);
}

struct node {
	ll mx, mxi, sv0, sv1, si0, si1, cnt, i1;
};

inline node operator + (const node &a, const node &b) {
	node res;
	if (a.mx > b.mx) {
		res.mx = a.mx;
		res.mxi = a.mxi;
	} else {
		res.mx = b.mx;
		res.mxi = b.mxi;
	}
	res.sv0 = a.sv0 + b.sv0;
	res.sv1 = a.sv1 + b.sv1;
	res.si0 = a.si0 + b.si0;
	res.si1 = a.si1 + b.si1;
	res.cnt = a.cnt + b.cnt;
	res.i1 = max(a.i1, b.i1);
	return res;
}

namespace SGT {
	ll add0[maxn << 2], add1[maxn << 2], tag[maxn << 2];
	node a[maxn << 2];
	
	inline void pushup(int x) {
		a[x] = a[x << 1] + a[x << 1 | 1];
	}
	
	inline void pushadd(int x, ll y, ll z) {
		a[x].sv0 += a[x].si0 * y;
		add0[x] += y;
		if (a[x].cnt) {
			a[x].mx += a[x].mxi * z;
			a[x].sv1 += a[x].si1 * z;
			add1[x] += z;
		}
	}
	
	inline void pushtag(int x, ll y) {
		if (!a[x].cnt) {
			return;
		}
		a[x].mx = y;
		a[x].mxi = a[x].i1;
		a[x].sv1 = y * a[x].cnt;
		add1[x] = 0;
		tag[x] = y;
	}
	
	inline void pushdown(int x) {
		if (tag[x] != -1) {
			pushtag(x << 1, tag[x]);
			pushtag(x << 1 | 1, tag[x]);
			tag[x] = -1;
		}
		if (add0[x] || add1[x]) {
			pushadd(x << 1, add0[x], add1[x]);
			pushadd(x << 1 | 1, add0[x], add1[x]);
			add0[x] = add1[x] = 0;
		}
	}
	
	void build(int rt, int l, int r) {
		a[rt].mx = -inf;
		tag[rt] = -1;
		if (l == r) {
			a[rt].sv0 = ::a[l];
			a[rt].si0 = l;
			return;
		}
		int mid = (l + r) >> 1;
		build(rt << 1, l, mid);
		build(rt << 1 | 1, mid + 1, r);
		pushup(rt);
	}
	
	void assign(int rt, int l, int r, int ql, int qr, ll x) {
		if (ql <= l && r <= qr) {
			pushtag(rt, x);
			return;
		}
		pushdown(rt);
		int mid = (l + r) >> 1;
		if (ql <= mid) {
			assign(rt << 1, l, mid, ql, qr, x);
		}
		if (qr > mid) {
			assign(rt << 1 | 1, mid + 1, r, ql, qr, x);
		}
		pushup(rt);
	}
	
	void update(int rt, int l, int r, int x) {
		if (l == r) {
			a[rt].mx = a[rt].sv1 = ::a[l];
			a[rt].mxi = a[rt].si1 = l;
			a[rt].sv0 = a[rt].si0 = 0;
			a[rt].cnt = 1;
			a[rt].i1 = l;
			return;
		}
		pushdown(rt);
		int mid = (l + r) >> 1;
		(x <= mid) ? update(rt << 1, l, mid, x) : update(rt << 1 | 1, mid + 1, r, x);
		pushup(rt);
	}
	
	ll query(int rt, int l, int r, int ql, int qr) {
		if (ql <= l && r <= qr) {
			return a[rt].sv0 + a[rt].sv1;
		}
		pushdown(rt);
		int mid = (l + r) >> 1;
		ll res = 0;
		if (ql <= mid) {
			res += query(rt << 1, l, mid, ql, qr);
		}
		if (qr > mid) {
			res += query(rt << 1 | 1, mid + 1, r, ql, qr);
		}
		return res;
	}
	
	int find(int rt, int l, int r, ll x) {
		if (a[rt].mx <= x) {
			return -1;
		}
		if (l == r) {
			return l;
		}
		pushdown(rt);
		int mid = (l + r) >> 1;
		if (a[rt << 1].mx > x) {
			return find(rt << 1, l, mid, x);
		} else {
			return find(rt << 1 | 1, mid + 1, r, x);
		}
	}
}

void solve() {
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &a[i]);
	}
	int cnt = 0;
	for (int i = 1; i <= m; ++i) {
		scanf("%lld", &qq[i].op);
		if (qq[i].op == 1) {
			scanf("%lld", &qq[i].x);
			p[++tot] = wwh(cnt, qq[i].x);
			id[tot] = i;
		} else if (qq[i].op == 2) {
			++cnt;
		} else {
			scanf("%lld%lld", &qq[i].x, &qq[i].y);
		}
	}
	vector<int> S;
	for (int i = 1; i <= n; ++i) {
		S.pb(i);
	}
	dfs(1, tot, S);
	for (int i = 1; i <= n; ++i) {
		vc[id[f[i]]].pb(i);
	}
	SGT::build(1, 1, n);
	for (int i = 1; i <= m; ++i) {
		if (qq[i].op == 1) {
			int p = SGT::find(1, 1, n, qq[i].x);
			if (p != -1) {
				SGT::assign(1, 1, n, p, n, qq[i].x);
			}
			for (int j : vc[i]) {
				a[j] = qq[i].x;
				SGT::update(1, 1, n, j);
			}
		} else if (qq[i].op == 2) {
			SGT::pushadd(1, 1, 1);
		} else {
			printf("%lld\n", SGT::query(1, 1, n, qq[i].x, qq[i].y));
		}
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。