AtCoder Beginner Contest 262 Ex Max Limited Sequence
先将 \(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;
}