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;
}

浙公网安备 33010602011771号