AtCoder Beginner Contest 262 Ex Max Limited Sequence

洛谷传送门

AtCoder 传送门

先将 \(m\) 和所有 \(X_i\)\(1\),转化为下界为 \(1\) 的问题。

考虑 \(q\) 条限制即要求 \(\forall j \in [L_i, R_i], A_j \le X_i\)\(\exists j \in [L_i, R_i], A_j = X_i\)。于是我们又可以得到每个位置的一个上界 \(B_j = \min\limits_{L_i \le j \le R_i} X_i\)。一个很重要的观察是,\(B_j \le X_i\),所以取到 \(X_i\) 的位置,只可能是 \(B_j = X_i\) 的位置。

于是我们每次将 \(B_j = X_i = k\) 的所有位置和限制单独拎出来计数,最后根据乘法原理,全部乘起来就是最终答案。现在问题变成了,有 \(t\) 个位置 \(1 \sim t\),对 \(A_{1 \sim t}\) 计数,满足 \(A_i \le m\),且给定若干条限制 \([L_j, R_j]\),需要满足 \(\exists i \in [L_j, R_j], A_i = m\)

考虑设计一个类似 Edu DP Contest W Intervals 的 dp,\(f_{i, j}\) 表示填完 \([1, i]\),离 \(i\) 最近的 \(j\) 使得 \(A_j = m\) ,并且满足所有右端点在 \(i\) 之前的限制的方案数。我们可以对于每个位置预处理一个 \(g_i = \max\limits_{R_j = i} L_j\) 表示 dp 到 \(i\) 时上一个 \(A_j = m\)\(j\) 必须 \(\ge i\)。有转移:

  • \(\forall j \le g_i, f_{i, j} \gets f_{i - 1, j} \times (m - 1)\),表示 \(A_i\) 可以填 \(1 \sim m - 1\) 的所有数,不必填 \(m\),因为已经满足以 \(i\) 为右端点的所有区间的需求了;
  • \(f_{i, i} \gets \sum\limits_{j = 0}^{i - 1} f_{i - 1, j}\),表示 \(A_i = m\),前面可以任意填。

直接暴力 dp 复杂度 \(O(n^2)\),已经可以通过 P4229 某位歌姬的故事了。这个东西显然也可以线段树优化的,每次转移相当于单点修改,区间乘、区间清空、查询整体和,而区间清空又可以转化成区间乘 \(0\),于是我们只需要维护支持单点修改、区间乘、查询整体和的数据结构即可,可以使用线段树。

时间复杂度 \(O((n + q) \log (n + q))\)

code
// Problem: Ex - Max Limited Sequence
// Contest: AtCoder - AtCoder Beginner Contest 262
// URL: https://atcoder.jp/contests/abc262/tasks/abc262_h
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

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

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

const int maxn = 200100;
const int logn = 20;
const ll mod = 998244353;

ll n, m, q, b[maxn], g[maxn], p[maxn], tt;
pii c[maxn];

struct node {
	ll l, r, x;
} a[maxn];

namespace DSU {
	int fa[maxn];
	
	inline void init() {
		for (int i = 1; i <= n + 1; ++i) {
			fa[i] = i;
		}
	}
	
	int find(int x) {
		return fa[x] == x ? x : fa[x] = find(fa[x]);
	}
}

namespace SGT {
	ll tree[maxn << 2], tag[maxn << 2];
	
	inline void pushup(int x) {
		tree[x] = (tree[x << 1] + tree[x << 1 | 1]) % mod;
	}
	
	inline void pushdown(int x) {
		if (tag[x] == 1) {
			return;
		}
		tree[x << 1] = tree[x << 1] * tag[x] % mod;
		tree[x << 1 | 1] = tree[x << 1 | 1] * tag[x] % mod;
		tag[x << 1] = tag[x << 1] * tag[x] % mod;
		tag[x << 1 | 1] = tag[x << 1 | 1] * tag[x] % mod;
		tag[x] = 1;
	}
	
	void build(int rt, int l, int r) {
		tree[rt] = 0;
		tag[rt] = 1;
		if (l == r) {
			return;
		}
		int mid = (l + r) >> 1;
		build(rt << 1, l, mid);
		build(rt << 1 | 1, mid + 1, r);
	}
	
	void update(int rt, int l, int r, int ql, int qr, ll x) {
		if (ql > qr) {
			return;
		}
		if (ql <= l && r <= qr) {
			tree[rt] = tree[rt] * x % mod;
			tag[rt] = tag[rt] * x % mod;
			return;
		}
		pushdown(rt);
		int mid = (l + r) >> 1;
		if (ql <= mid) {
			update(rt << 1, l, mid, ql, qr, x);
		}
		if (qr > mid) {
			update(rt << 1 | 1, mid + 1, r, ql, qr, x);
		}
		pushup(rt);
	}
	
	void modify(int rt, int l, int r, int x, ll y) {
		if (l == r) {
			tree[rt] = y;
			return;
		}
		pushdown(rt);
		int mid = (l + r) >> 1;
		if (x <= mid) {
			modify(rt << 1, l, mid, x, y);
		} else {
			modify(rt << 1 | 1, mid + 1, r, x, y);
		}
		pushup(rt);
	}
}

namespace ST {
	ll f[maxn][logn];
	
	inline void init() {
		for (int i = 1; i <= n; ++i) {
			f[i][0] = b[i];
		}
		for (int j = 1; (1 << j) <= n; ++j) {
			for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
				f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
			}
		}
	}
	
	inline ll query(int l, int r) {
		int k = __lg(r - l + 1);
		return max(f[l][k], f[r - (1 << k) + 1][k]);
	}
}

inline ll calc(ll m) {
	SGT::build(1, 0, tt);
	SGT::modify(1, 0, tt, 0, 1);
	for (int i = 1; i <= tt; ++i) {
		ll s = SGT::tree[1];
		SGT::update(1, 0, tt, g[i], tt, m - 1);
		SGT::update(1, 0, tt, 0, g[i] - 1, 0);
		SGT::modify(1, 0, tt, i, s);
	}
	return SGT::tree[1];
}

void solve() {
	scanf("%lld%lld%lld", &n, &m, &q);
	++m;
	for (int i = 1; i <= q; ++i) {
		scanf("%lld%lld%lld", &a[i].l, &a[i].r, &a[i].x);
		++a[i].x;
	}
	sort(a + 1, a + q + 1, [&](node a, node b) {
		return a.x < b.x;
	});
	for (int i = 1; i <= n; ++i) {
		b[i] = m + 1;
	}
	DSU::init();
	for (int i = 1; i <= q; ++i) {
		int l = a[i].l, r = a[i].r, x = a[i].x;
		for (int j = DSU::find(l); j <= r; j = DSU::find(j)) {
			b[j] = x;
			DSU::fa[j] = j + 1;
		}
	}
	ST::init();
	for (int i = 1; i <= q; ++i) {
		int l = a[i].l, r = a[i].r, x = a[i].x;
		if (ST::query(l, r) < x) {
			puts("0");
			return;
		}
	}
	for (int i = 1; i <= n; ++i) {
		c[i] = make_pair(b[i], i);
	}
	sort(c + 1, c + n + 1);
	ll ans = 1;
	for (int i = 1, j = 1, k = 1; i <= n; i = (++j)) {
		if (c[i].fst > m) {
			continue;
		}
		while (j < n && c[j + 1].fst == c[i].fst) {
			++j;
		}
		tt = 0;
		for (int x = i; x <= j; ++x) {
			p[++tt] = c[x].scd;
		}
		sort(p + 1, p + tt + 1);
		for (int x = 1; x <= tt; ++x) {
			g[x] = 0;
		}
		while (k <= q && a[k].x <= c[i].fst) {
			if (a[k].x < c[i].fst) {
				++k;
				continue;
			}
			ll l = lower_bound(p + 1, p + tt + 1, a[k].l) - p;
			ll r = upper_bound(p + 1, p + tt + 1, a[k].r) - p - 1;
			if (l <= r) {
				g[r] = max(g[r], l);
			}
			++k;
		}
		ans = ans * calc(c[i].fst) % mod;
	}
	for (int i = 1; i <= n; ++i) {
		if (b[i] > m) {
			ans = ans * m % mod;
		}
	}
	printf("%lld\n", ans);
}

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

posted @ 2023-06-25 10:45  zltzlt  阅读(10)  评论(0编辑  收藏  举报