洛谷 [DTCPC2024] mex,min,max 题解

Description

给你一个数列 \(\{a_n\}\) 和正整数 \(k\),求有几组 \([l,r]\) 满足 \(\text{mex}(l,r)+\min(l,r)+k\geq\max(l,r)\)

Solution

来一发不用脑子的做法。

Part I

观察到一个性质:对于一个区间,\(\text{mex}=0\)\(\min=0\) 至少成立一个。证明显然。

\(A\)\(\text{mex}+k\geq\max\) 的区间个数,\(B\)\(\min+k\geq\max\) 的区间个数,\(C\)\(\max\leq k\) 的区间个数。

根据性质,简单容斥一下,答案 \(ans=A+B-C\)

\(B,C\) 好求,因为固定了右端点后合法的左端点在一个区间内,枚举右端点二分左端点即可。这部分用 ST 表处理极值,可以做到 \(\mathcal{O}(n\log n)\)

考虑怎么求 \(A\)

Part II

你如果做过 [THUPC 2024 初赛] 套娃 的话,就会知道一个叫『极小 mex 区间』的东西。

一个结论是这样的『极小 mex 区间』只有 \(\mathcal{O}(n)\) 个。所以用那题的做法求出所有『极小 mex 区间』,并对每个『极小 mex 区间』求出包含它的『极大 mex 区间』。这部分可以做到 \(\mathcal{O}(n\log n)\)

枚举所有极小区间。对于一个极小区间 \([l,r]\),它的 \(\text{mex}\)\(p\),极大区间为 \([L,R]\)。那么对于 \(\forall l'\in[L,l],r'\in[r,R]\)\(\text{mex}(l',r')=p\)。这样,\(\text{mex}+k\) 就固定了。二分出极大的区间 \([l',r']\),使得 \(l'\in[L,l],r'\in[r,R]\)\(p+k\geq \max(l'\,r')\)\(\forall pl\in[l',l],pr\in[r,r']\)\([pl,pr]\) 是合法区间。

我们发现这样求合法区间不漏但是会重。不过没关系,直接扫描线,对所有 \((l',l)(r',r)\) 组成的矩阵求面积并就行了。这部分复杂度为 \(\mathcal{O}(n\log n)\)

总复杂度 \(\mathcal{O}(n\log n)\),可以通过。码量其实没有想象中多,只有 4K 不到,而且好调。

实现的时候二分容易出错,所以把二分换成了更简洁的倍增。

Code

// writer: Milkcat
// time: 2024.2.17
#include <bits/stdc++.h>
#define REP(i, l, r) for (int i = (l); i <= (r); ++ i)
#define DEP(i, r, l) for (int i = (r); i >= (l); -- i)
#define fi first
#define se second
#define pb emplace_back
#define mems(x, v) memset((x), (v), sizeof(x))
using namespace std;
namespace Milkcat {
	typedef long long LL;
	typedef pair<LL, LL> pii;
	const int N = 5e5 + 5;
	
	namespace HJT {
		int cnt, s[N << 5], Ls[N << 5], Rs[N << 5];
		int modify(int p, int l, int r, int t, int k) {
			int rt = ++ cnt; Ls[rt] = Ls[p], Rs[rt] = Rs[p];
			if (l == r) return s[rt] = k, rt;
			int mid = (l + r) >> 1;
			if (t <= mid) Ls[rt] = modify(Ls[p], l, mid, t, k);
			if (t > mid) Rs[rt] = modify(Rs[p], mid + 1, r, t, k);
			s[rt] = min(s[Ls[rt]], s[Rs[rt]]);
			return rt;
		}
		int query(int p, int l, int r, int k) {
			if (l == r) return l;
			int mid = (l + r) >> 1;
			if (s[Ls[p]] < k) return query(Ls[p], l, mid, k);
			return query(Rs[p], mid + 1, r, k);
		}
	}
	
	namespace SGT {
		LL sum[N << 3], ass[N << 3];
		void pushup(int p, int l, int r) {
			if (ass[p]) sum[p] = r - l + 1;
			else sum[p] = sum[p << 1] + sum[p << 1 | 1]; 
		}
		void upd(int p, int l, int r, int nl, int nr, int k) {
			if (nl <= l && r <= nr) { ass[p] += k, pushup(p, l, r); return; }
			int mid = (l + r) >> 1;
			if (nl <= mid) upd(p << 1, l, mid, nl, nr, k);
			if (nr > mid) upd(p << 1 | 1, mid + 1, r, nl, nr, k);
			pushup(p, l, r);
		}
	}
	
	int n, k, a[N], rt[N], cnt[N], mn[22][N], mx[22][N]; LL ans;
	vector<pii> g[N]; vector<int> vec[N];
	vector<tuple<int, int, int>> s[N];
	
	int mex(int l, int r) { return HJT::query(rt[r], 0, n, l); }
    
	int main() {
		cin >> n >> k;
		REP(i, 1, n) {
			cin >> a[i], mx[0][i] = mn[0][i] = a[i];
			rt[i] = HJT::modify(rt[i - 1], 0, n, a[i], i);
			vec[a[i]].pb(i), g[a[i] > 0 ? 0 : 1].pb(pii(i, i));
		}
		
		auto Pre = [&](int t, int x) {
			auto it = lower_bound(vec[x].begin(), vec[x].end(), t);
			return (it == vec[x].begin() ? 0 : *prev(it)); 
		};
		auto Nxt = [&](int t, int x) {
			auto it = upper_bound(vec[x].begin(), vec[x].end(), t);
			return (it == vec[x].end() ? n + 1 : *it);
		};
		REP(i, 0, n) {
			for (pii t : g[i - 1]) {
				int l = Pre(t.fi, i - 1), r = Nxt(t.se, i - 1);
				if (l >= 1) g[mex(l, t.se)].pb(pii(l, t.se));
				if (r <= n) g[mex(t.fi, r)].pb(pii(t.fi, r));
			}
			vector<pii> tmp; int las = 1e9;
			sort(g[i].begin(), g[i].end(), [&](pii x, pii y) { return x.fi == y.fi ? x.se < y.se : x.fi > y.fi; });
			for (auto t : g[i]) if (las > t.se) tmp.pb(t), las = t.se;
			g[i].swap(tmp);
		}
		
		REP(j, 1, __lg(n)) REP(i, 1, n - (1 << j) + 1) {
			mx[j][i] = max(mx[j - 1][i], mx[j - 1][i + (1 << (j - 1))]);
			mn[j][i] = min(mn[j - 1][i], mn[j - 1][i + (1 << (j - 1))]);
		}
		auto ask = [&](int l, int r) -> pii {
			int d = __lg(r - l + 1);
			return {max(mx[d][l], mx[d][r - (1 << d) + 1]), min(mn[d][l], mn[d][r - (1 << d) + 1])};
		};
		
		REP(i, 0, n) for (auto [l, r] : g[i]) {
			if (i + k < ask(l, r).fi) continue;
			int L = l + 1, R = r - 1, Lm = Pre(l, i) + 1, Rm = Nxt(r, i) - 1;
			DEP(j, __lg(n), 0) {
				int t = L - (1 << j);
				if (t >= Lm && i + k >= ask(t, l).fi) L = t;
			}
			DEP(j, __lg(n), 0) {
				int t = R + (1 << j);
				if (t <= Rm && i + k >= ask(r, t).fi) R = t;
			}
			s[L].pb(r, R, 1), s[l + 1].pb(r, R, -1);
		}
		REP(i, 1, n) {
			for (auto [l, r, v] : s[i]) SGT::upd(1, 1, n, l, r, v);
			ans += SGT::sum[1];
		}
		
		
		REP(R, 1, n) {
			int L = R + 1;
			DEP(i, __lg(n), 0) {
				int t = L - (1 << i);
				auto [mx, mn] = ask(t, R);
				if (t >= 1 && mx - mn <= k) L = t;
			}
			ans += R - L + 1;
		}
		REP(R, 1, n) {
			int L = R + 1;
			DEP(i, __lg(n), 0) {
				int t = L - (1 << i);
				if (t >= 1 && ask(t, R).fi <= k) L = t;
			}
			ans -= R - L + 1;
		}
		
		cout << ans << '\n';
		
        return 0;
    }
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int T = 1;
	while (T --) Milkcat::main();
	return 0;
}

posted @ 2024-02-17 15:45  喵仔牛奶  阅读(47)  评论(0编辑  收藏  举报