AT_abc405_g [ABC405G] Range Shuffle Query 题解

题目传送门

思路

\(c_i\) 为颜色 \(i\) 在询问的 \((l, r)\) 的区间中出现的次数。则答案即为:

\[\displaystyle \frac{\begin{aligned}(\sum_{i = 1}^{x - 1}{c_i})!\end{aligned}}{\begin{aligned} \prod_{i = 1}^{x - 1} c_i ! \end{aligned}} \]

其中,求出 \(c_i\) 很简单,是一个莫队的板子。难点在于这个 \(x\) 的限制。上面的 \(\sum \limits_{i = 1}^{x - 1}c_i\) 可以直接用一个树状数组维护区间和即可。而下面的 \(\prod \limits_{i = 1}^{x - 1}c_i!\) 稍微复杂一些。每一次用莫队时,只会有一个答案发生修改,且若原本的数量为 \(c_i\),则修改后的 \(c'_i\) 一定会满足 \(|c_i - c'_i| = 1\)。那所以只用维护区间积即可。每一次也是单点修改。时间复杂度 \(\mathcal{O}(q \sqrt{m} \log n)\)

但是这样会超时。出题人专门出了 anti_fenwick 卡树状数组。

考虑进一步优化,也就是将这个 \(\mathcal{O}(\log n)\) 优化掉。我们发现,一共最多会有 \(q \sqrt{n}\) 次修改,\(q\) 次查询。要做成 \(\mathcal{O}(q \sqrt{n})\),就需要 \(\mathcal{O}(1)\) 单点修改,\(\mathcal{O}(\sqrt{n})\) 查询。

这个数据结构叫分块。可以用分块维护区间和和区间积。这样就做完了。时间复杂度 \(\mathcal{O}(q \sqrt{n})\)

代码中块长取的 \(\sqrt{n}\)本题卡常

代码

#include <bits/stdc++.h>
#define int long long
#define lowbit(x) (x & (-x)) 
using namespace std;

const int N = 250005, mod = 998244353;

struct query
{
	int l, r, x, id;
} q[N];

int n, m, bl;
int a[N], fact[N], inv[N], cnt[N], ans[N], bel[N];

struct block
{
	int x[N], res[N];
	void init()
	{
		for (int i = 1; i < N; i++) x[i] = res[i] = 1;
	}
	void modifya(int u, int y)
	{
		res[bel[u]] = (res[bel[u]] + y) % mod;
		x[u] = (x[u] + y) % mod;
	}
	int querys(int r)
	{
		if (r <= 0) return 0;
		int R = bel[r], tmp = 0;
		for (int i = 1; i < R; i++)
			tmp = (tmp + res[i]) % mod;
		for (int i = (R - 1) * bl + 1; i <= r; i++)
			tmp = (tmp + x[i]) % mod;
		return tmp;
	}
	void modifym(int u, int y)
	{
		res[bel[u]] = (res[bel[u]] * y) % mod;
		x[u] = (x[u] * y) % mod;
		if (res[bel[u]] == 0) res[bel[u]] = 1;
		if (x[u] == 0) x[u] = 1;
	}
	int querym(int r)
	{
		if (r <= 0) return 1;
		int R = bel[r], tmp = 1;
		for (int i = 1; i < R; i++)
			tmp = (tmp * res[i]) % mod;
		for (int i = (R - 1) * bl + 1; i <= r; i++)
			tmp = (tmp * x[i]) % mod;
		return tmp;
	}
} blm, bls;

inline int read()
{
	int x = 0, f = 1;
	char ch = getchar_unlocked();
	while (ch < '0' || ch > '9')
	{
		if (ch == '-') f = -1;
		ch = getchar_unlocked();
	}
	while (ch >= '0' && ch <= '9')
	{
		x = x * 10 + ch - 48;
		ch = getchar_unlocked();
	}
	return x * f;
}

bool cmp(query x, query y)
{
	if (x.l / bl != y.l / bl) return x.l < y.l;
	if (x.l / bl % 2 == 0) return x.r < y.r;
	return x.r > y.r;
}

void add(int x)
{
	cnt[x]++;
	blm.modifym(x, inv[cnt[x]]);
	bls.modifya(x, 1);
}

void del(int x)
{
	blm.modifym(x, cnt[x]);
	bls.modifya(x, -1);
	cnt[x]--;
}

signed main()
{
	fact[0] = 1;
	for (int i = 1; i < N; i++)
		fact[i] = fact[i - 1] * i % mod;
	inv[1] = 1;
	for (int i = 2; i < N; i++)
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
	n = read(), m = read();
	bl = (int)sqrt(n);
	if (bl == 0) bl = 1;
	for (int i = 1; i <= n; i++)
		a[i] = read();
	for (int i = 1; i <= n; i++)
		bel[i] = (i - 1) / bl + 1;
	for (int i = 1; i <= m; i++)
	{
		q[i].l = read(), q[i].r = read(), q[i].x = read();
		q[i].id = i;
	}
	sort(q + 1, q + m + 1, cmp);
	blm.init();
	int l = 1, r = 0;
	for (int i = 1; i <= m; i++)
	{
		while (l > q[i].l) add(a[--l]);
		while (r < q[i].r) add(a[++r]);
		while (l < q[i].l) del(a[l++]);
		while (r > q[i].r) del(a[r--]);
		int now = bls.querys(q[i].x - 1), res = 0;
		if (now > 0) res = fact[now] * blm.querym(q[i].x - 1) % mod;
		else res = 1;
		ans[q[i].id] = res;
	}
	for (int i = 1; i <= m; i++)
		printf("%lld\n", ans[i]);
	return 0;
}
posted @ 2026-02-03 19:08  lucasincyber  阅读(0)  评论(0)    收藏  举报