题解:QOJ7899 Say Hello to the Future

题意

对于一个长度为 \(n\) 的序列 \(a\),定义 \(f(a)\) 为将其划段的方案数,使得对于每一段 \(a[l,r]\) 都有 \(r-l+1\geq\max\limits_{i=l}^ra_i\)

现在给定一个长度为 \(n\) 的序列 \(a\),对于每个位置 \(i=1,2,\cdots,n\),设 \(b\) 为令 \(a_i\leftarrow 1\) 后的序列,求 \(f(b)\) 的值。答案对 \(998244353\) 取模。\(1\leq a_i\leq n\leq 2\times 10^5\)

题解

NOIP 模拟赛 T4。

先考虑如何在原序列上 DP。令 \(f_i\) 表示对 \(a[1,i]\) 划段的方案数,暴力枚举所有合法的 \(j\) 转移。这样单次 DP 就是 \(\mathcal{O}(n^2)\) 的。我们发现数据结构难以优化,考虑使用 CDQ 分治优化。具体地,设当前分治区间为 \([l,r]\),中点为 \(mid=\left\lfloor\dfrac{l+r}{2}\right\rfloor\)。我们预处理出 \(mxL_i=\max\limits_{j=i}^{mid}a_j\)\(mxR_i=\max\limits_{j=mid+1}^ia_j\),然后枚举右端点 \(mid<R\leq r\),考虑哪些跨过 \(mid\) 的区间 \([L,R]\) 是合法的,这样的区间会给 \(f_R\) 加上 \(f_{L-1}\) 的贡献。

显然区间合法的条件就是 \(R-L+1\geq\max(mxL_L,mxR_R)\),把它拆成 \(R-L+1\geq mxL_L\land R-L+1\geq mxR_R\),整理可得

\[R\geq mxL_L+L-1\land L\leq R-mxR_R+1 \]

这就变成二维数点的形式了,用 BIT 即可 \(\mathcal{O}(n\log{n})\) 维护。边界为 \(l=r\) 时,若 \(a_l=1\),则令 \(f_{l}\leftarrow f_{l-1}\)

时间复杂度为 \(\mathcal{O}(n\log^2{n})\)

回到原题,考察修改操作的影响。我们先类似地计算出 \(g_i\) 表示对 \(a[i,n]\) 划段的方案数。可以发现,若令 \(a_i\leftarrow 1\) 后,某个区间 \(i\in[l,r]\) 从不合法变为合法,那么其就会给答案带来 \(f_{l-1}g_{r+1}\) 的增量,于是我们考虑如何快速维护出这些增量信息。

同样考虑分治。不难发现,对于某个区间 \([l,r]\),当且仅当 \(a_i\)\(a[l,r]\)严格最大值时,令 \(a_i\leftarrow 1\) 才有可能改变 \(a[l,r]\) 的合法性。更进一步地,再考察 \(a[l,r]\) 中的次大值 \(smx\),那么 \(a[l,r]\) 会在 \(a_i\leftarrow 1\) 时从不合法变为合法,当且仅当 \(a_i\)\(a[l,r]\)严格最大值,且 \(\bm{smx}\leq r-l+1<a_i\)

考虑在分治过程中分类讨论严格最大值 \(a_p\) 出现在左侧还是右侧,这里以 \(p\in[l,mid]\) 为例。我们倒序枚举区间左端点 \(L\),实时维护出 \(a[L,mid]\) 中的最大值 \(mx\)、最大值的位置 \(p\) 以及次大值 \(smx\),此时的增量会贡献到 \(\Delta_p\) 的位置。根据前面的讨论,不难得出 \([L,R]\) 从不合法变为合法的条件为

\[smx+L-1\leq R< mx+L-1\land L\leq R-mxR_R+1 \]

那么实际上我们要令

\[\Delta_p\leftarrow \Delta_p+f_{L-1}\sum_{\substack{smx+L-1\leq R< mx+L-1\\l\leq R-mxR_i+1}}g_{R+1} \]

还是二维数点的形式!我们将这个 \(\text{3-side}\) 矩形差分成 \(\text{2-side}\) 的形式,对右端点倒序扫描线后 BIT 维护点权和即可。边界为 \(l=r\) 时,若 \(a_l\neq 1\) 则令 \(\Delta_l\leftarrow f_{l-1}g_{l+1}\)

\(p\in[mid+1,r]\) 的情况是类似的。

时间复杂度 \(\mathcal{O}(n\log^2{n})\)

代码

代码并不好写,有很多细节。

#include <iostream>
#include <algorithm>

using namespace std;

#define lowbit(x) ((x) & -(x))
#define chk_min(x, v) (x) = min((x), (v))
#define chk_max(x, v) (x) = max((x), (v))
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5, MOD = 998244353;

inline int add(int x, int y) { return x += y, x >= MOD ? x - MOD : x; }
inline int sub(int x, int y) { return x -= y, x < 0 ? x + MOD : x; }
inline void cadd(int &x, int y) { x += y, x < MOD || (x -= MOD); }
inline void csub(int &x, int y) { x -= y, x < 0 && (x += MOD); }

int n, a[N], f[N], g[N], d[N];
int mxl[N], mxr[N];
struct Node { int id, x, v; } nd[N];
struct Query { int id, x, p, v, s; } qr[N << 1];

struct BIT {
	int c[N];
	inline int query(int x) {
		ll res = 0;
		for (; x; x -= lowbit(x)) res += c[x];
		return res % MOD;
	}
	inline void add(int x, int v) { for (; x <= n; x += lowbit(x)) cadd(c[x], v); }
} ft;

inline void dp(int l, int r, int *f) {
	if (l == r) {
		if (a[l] == 1) cadd(f[l], f[l - 1]);
		return;
	}
	int mid = (l + r) >> 1;
	dp(l, mid, f);
	mxl[mid + 1] = mxr[mid] = 0;
	for (int i = mid; i >= l; --i) mxl[i] = max(mxl[i + 1], a[i]);
	for (int i = mid + 1; i <= r; ++i) mxr[i] = max(mxr[i - 1], a[i]);
	for (int i = l; i <= mid; ++i) nd[i] = {i, max(mxl[i] + i - 1, mid + 1), f[i - 1]};
	sort(nd + l, nd + mid + 1, [](const Node &x, const Node &y) { return x.x < y.x; });
	for (int i = mid + 1, j = l; i <= r; ++i) {
		while (j <= mid && nd[j].x <= i) ft.add(nd[j].id, nd[j].v), ++j;
		if (i - mxr[i] + 1 >= l) cadd(f[i], ft.query(min(i - mxr[i] + 1, mid)));
	}
	for (int i = l; i <= mid; ++i) if (nd[i].x <= r) ft.add(nd[i].id, sub(0, nd[i].v));
	dp(mid + 1, r, f);
}
inline void solve(int l, int r) {
	if (l == r) {
		if (a[l] ^ 1) cadd(d[l], (ll)f[l - 1] * g[r + 1] % MOD);
		return;
	}
	int mid = (l + r) >> 1;
	solve(l, mid);
	mxl[mid + 1] = mxr[mid] = 0;
	for (int i = mid; i >= l; --i) mxl[i] = max(mxl[i + 1], a[i]);
	for (int i = mid + 1; i <= r; ++i) mxr[i] = max(mxr[i - 1], a[i]);
	int mx = 0, smx = 0, p = 0, sz = 0;
	for (int i = mid; i >= l; --i) {
		if (a[i] > mx) smx = mx, mx = a[i], p = i;
		else if (a[i] > smx) smx = a[i];
		if (smx + i - 1 <= r) qr[++sz] = {p, max(smx + i - 1, mid + 1), i, f[i - 1], 1};
		if (mx + i - 1 <= r) qr[++sz] = {p, max(mx + i - 1, mid + 1), i, f[i - 1], -1};
	}
	sort(qr + 1, qr + sz + 1, [](const Query &x, const Query &y) { return x.x > y.x; });
	for (int i = r, j = 1; i > mid; --i) {
		if (i - mxr[i] + 1 >= l) ft.add(min(i - mxr[i] + 1, mid), g[i + 1]);
		while (j <= sz && qr[j].x >= i) {
			int val = sub(ft.query(n), ft.query(qr[j].p - 1));
			if (qr[j].s == -1) val = sub(0, val);
			cadd(d[qr[j].id], (ll)qr[j].v * val % MOD);
			++j;
		}
	}
	for (int i = mid + 1; i <= r; ++i)
		if (i - mxr[i] + 1 >= l) ft.add(min(i - mxr[i] + 1, mid), sub(0, g[i + 1]));
	mx = smx = p = sz = 0;
	for (int i = mid + 1; i <= r; ++i) {
		if (a[i] > mx) smx = mx, mx = a[i], p = i;
		else if (a[i] > smx) smx = a[i];
		if (i - smx + 1 >= l) qr[++sz] = {p, min(i - smx + 1, mid), i, g[i + 1], 1};
		if (i - mx + 1 >= l) qr[++sz] = {p, min(i - mx + 1, mid), i, g[i + 1], -1};
	}
	sort(qr + 1, qr + sz + 1, [](const Query &x, const Query &y) { return x.x < y.x; });
	for (int i = l, j = 1; i <= mid; ++i) {
		if (mxl[i] + i - 1 <= r) ft.add(max(mxl[i] + i - 1, mid + 1), f[i - 1]);
		while (j <= sz && qr[j].x <= i) {
			int val = ft.query(qr[j].p);
			if (qr[j].s == -1) val = sub(0, val);
			cadd(d[qr[j].id], (ll)qr[j].v * val % MOD);
			++j;
		}
	}
	for (int i = l; i <= mid; ++i)
		if (mxl[i] + i - 1 <= r) ft.add(max(mxl[i] + i - 1, mid + 1), sub(0, f[i - 1]));
	solve(mid + 1, r);
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    f[0] = 1, dp(1, n, f);
    reverse(a + 1, a + n + 1);
    g[0] = 1, dp(1, n, g);
    reverse(g, g + n + 2), reverse(a + 1, a + n + 1);
    solve(1, n);
    for (int i = 1; i <= n; ++i) cout << add(f[n], d[i]) << ' ';
    return 0;
}
posted @ 2025-08-10 01:31  P2441M  阅读(43)  评论(0)    收藏  举报