AtCoder Beginner Contest 262 Ex Max Limited Sequence

洛谷传送门

AtCoder 传送门

先将 m 和所有 Xi1,转化为下界为 1 的问题。

考虑 q 条限制即要求 j[Li,Ri],AjXij[Li,Ri],Aj=Xi。于是我们又可以得到每个位置的一个上界 Bj=minLijRiXi。一个很重要的观察是,BjXi,所以取到 Xi 的位置,只可能是 Bj=Xi 的位置。

于是我们每次将 Bj=Xi=k 的所有位置和限制单独拎出来计数,最后根据乘法原理,全部乘起来就是最终答案。现在问题变成了,有 t 个位置 1t,对 A1t 计数,满足 Aim,且给定若干条限制 [Lj,Rj],需要满足 i[Lj,Rj],Ai=m

考虑设计一个类似 Edu DP Contest W Intervals 的 dp,fi,j 表示填完 [1,i],离 i 最近的 j 使得 Aj=m ,并且满足所有右端点在 i 之前的限制的方案数。我们可以对于每个位置预处理一个 gi=maxRj=iLj 表示 dp 到 i 时上一个 Aj=mj 必须 i。有转移:

  • jgi,fi,jfi1,j×(m1),表示 Ai 可以填 1m1 的所有数,不必填 m,因为已经满足以 i 为右端点的所有区间的需求了;
  • fi,ij=0i1fi1,j,表示 Ai=m,前面可以任意填。

直接暴力 dp 复杂度 O(n2),已经可以通过 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 @   zltzlt  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示