AtCoder Beginner Contest 262 Ex Max Limited Sequence
先将 和所有 加 ,转化为下界为 的问题。
考虑 条限制即要求 且 。于是我们又可以得到每个位置的一个上界 。一个很重要的观察是,,所以取到 的位置,只可能是 的位置。
于是我们每次将 的所有位置和限制单独拎出来计数,最后根据乘法原理,全部乘起来就是最终答案。现在问题变成了,有 个位置 ,对 计数,满足 ,且给定若干条限制 ,需要满足 。
考虑设计一个类似 Edu DP Contest W Intervals 的 dp, 表示填完 ,离 最近的 使得 ,并且满足所有右端点在 之前的限制的方案数。我们可以对于每个位置预处理一个 表示 dp 到 时上一个 的 必须 。有转移:
- ,表示 可以填 的所有数,不必填 ,因为已经满足以 为右端点的所有区间的需求了;
- ,表示 ,前面可以任意填。
直接暴力 dp 复杂度 ,已经可以通过 P4229 某位歌姬的故事了。这个东西显然也可以线段树优化的,每次转移相当于单点修改,区间乘、区间清空、查询整体和,而区间清空又可以转化成区间乘 ,于是我们只需要维护支持单点修改、区间乘、查询整体和的数据结构即可,可以使用线段树。
时间复杂度 。
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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话