题解——星之界
题解
考虑化简这个可恶的式子
拆开,设
现在我们需要一个维护区间和和区间阶乘积并且支持区间定值修改的数据结构
貌似线段树之类的不行,考虑分块实际上是看见了1e5
统计比较容易,主要难点在于区间定值修改。注意到值域与
但是一个桶只能表示这个值出现的数量,为了维护
核心代码
struct node {
int id, cnt;
}f[S][N];//桶
struct Node {
int sum, mul;
};
int get(int x) {
return x == fa[x] ? x : fa[x] = get(fa[x]);
}
inline void init(int id) {//重置这个块
mul[id] = 1;
sum[id] = 0;
for (re int i = L[id]; i <= R[id]; i++) {
if (f[id][a[i]].id) {
f[id][a[i]].cnt++;
fa[i] = f[id][a[i]].id;
}
else {
f[id][a[i]].id = i;
fa[i] = i;
val[i] = a[i];//val是真实值
f[id][a[i]].cnt = 1;
}
sum[id] += a[i];
mul[id] = 1ll * mul[id] * inv[a[i]] % p;
}
}
inline void clear(int id) {
for (re int i = L[id]; i <= R[id]; i++) {
a[i] = val[get(i)];
f[id][a[i]].cnt = f[id][a[i]].id = 0;
}
for (re int i = L[id]; i <= R[id]; i++) {
fa[i] = 0;
}
}
inline void change_only(int id, int l, int r, int x, int y) {
clear(id);
for (re int i = l; i <= r; i++) {
if (a[i] == x)a[i] = y;
}
init(id);
}
inline void change_all(int id, int x, int y) {
f[id][y].cnt += f[id][x].cnt;
sum[id] -= (x - y) * f[id][x].cnt;
mul[id] = 1ll * mul[id] * power_jc[x][f[id][x].cnt] % p * power_inv[y][f[id][x].cnt] % p;//为了保证复杂度,预处理阶乘及其逆元的幂
if (f[id][y].id == 0)f[id][y].id = f[id][x].id, val[f[id][x].id] = y;
else fa[f[id][x].id] = f[id][y].id;
f[id][x] = { 0,0 };
}
inline void change(int l, int r, int x, int y) {
if (pos[l] == pos[r]) {
change_only(pos[l], l, r, x, y);
return;
}
change_only(pos[l], l, R[pos[l]], x, y);
change_only(pos[r], L[pos[r]], r, x, y);
for (re int i = pos[l] + 1; i < pos[r]; i++)change_all(i, x, y);
}
查询的时候就很简单,将涉及到的积乘上,将和加上
inline Node find_only(int l, int r) {
Node ans = { 0,1 };
for (re int i = l; i <= r; i++) {
ans.sum += val[get(i)];
ans.mul = 1ll * ans.mul * inv[val[get(i)]] % p;
}
return ans;
}
inline Node find_all(int id) {
return { sum[id],mul[id] % p };
}
inline Node merge(Node a, Node b) {
return { a.sum + b.sum,1ll * a.mul * b.mul % p };
}
inline int find(int l, int r) {
if (pos[l] == pos[r]) {
Node ans = find_only(l, r);
return 1ll * jc[ans.sum] * ans.mul % p;
}
Node ans = merge(find_only(l, R[pos[l]]), find_only(L[pos[r]], r));
for (re int i = pos[l] + 1; i < pos[r]; i++) {
ans = merge(ans, find_all(i));
}
return 1ll * jc[ans.sum] * ans.mul % p;
}
预处理的时候就常规预处理,然后处理阶乘逆元,预处理他们的幂保证复杂度(反正空间够),注意逆元得递推求
inline void init() {
jc[1] = inv[1] = 1;
for (re int i = 2; i <= M - 50; i++) {
jc[i] = 1ll * jc[i - 1] * i % p;
inv[i] = p - (1ll * p / (1ll * i) * 1ll * inv[p % i] % p) % p;
}
for (re int i = 2; i <= M - 50; i++) {
inv[i] = 1ll * inv[i - 1] * inv[i] % p;
}
scanf("%d%d", &n, &m);
for (re int i = 1; i <= n; i++)scanf("%d", &a[i]);
block = sqrt(n);
siz = n % block ? n / block + 1 : n / block;
for (re int i = 1; i <= siz; i++) {
L[i] = (i - 1) * block + 1;
R[i] = min(i * block, n);
}
for (re int i = 1; i <= siz; i++) {
for (re int j = L[i]; j <= R[i]; j++) {
pos[j] = i;
}
}
for (re int i = 1; i <= N - 5; i++) {
power_jc[i][0] = power_inv[i][0] = 1;
for (re int j = 1; j <= block; j++) {
power_jc[i][j] = 1ll * power_jc[i][j - 1] * jc[i] % p;
power_inv[i][j] = 1ll * power_inv[i][j - 1] * inv[i] % p;
}
}
for (re int i = 1; i <= siz; i++)init(i);
}
注意有个细节(没注意到就是25pts,调我好久,还是看题解才发现的),当修改时
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!