P9989 [Ynoi Easy Round 2023] TEST_69
[Ynoi Easy Round 2023] TEST_69
题目描述
给定一个长为 \(n\) 的序列 \(a\),有 \(m\) 次操作。
每次有两种操作:
1 l r x
:对于区间 \([l,r]\) 内所有 \(i\),将 \(a_i\) 变成 \(\gcd(a_i,x)\)。
2 l r
:查询区间 \([l,r]\) 的和,答案对 \(2^{32}\) 取模后输出。
对于 \(100\%\) 的数据,满足 \(1\le n\le 2\cdot 10^5,1\le m\le 5 \cdot 10^5\),所有数值为 \([1,10^{18}]\) 内的整数。
Solution
很典的套路,不愧是 Easy Round。
考虑 \(1\) 操作有什么性质,容易发现每次成功的变化过后 \(a_i\) 一定会变成自己的一个因数。从分解质因数的角度来看的话这个数的所有质因子的次数一定至少会有一个减少 \(1\)。而一个数的质因子次数之和很明显是 \(\log v\) 级别的,因此如果能保证每次修改都一定能够修改到一个数字的话,总共的修改次数就是总的个数乘上 \(\log v\) 的。
这就和区间开方、区间 \(\log\) 变成了同一个东西了,使用线段树维护,如果可以修改这个区间内的数就直接递归下去做,否则直接回溯。时间复杂度不难做到 \(\mathcal O(n\log n\log v)\)。
那么只需要判定这个区间内是否存在一个数可以被修改了。考虑如果一个区间无法被更改会有什么性质,不难发现这个区间内的所有数都应该是修改值的因数。也就是说,修改值一定是区间内所有数的 \(\operatorname{lcm}\)。所以只需要维护一下区间 \(\operatorname{lcm}\) 就可以判断了。
题面值域是 \(10^{18}\),所以计算 \(\operatorname{lcm}\) 时应当先除后乘,否则会爆 long long
。另外,如果一个区间的 \(\operatorname{lcm}>10^{18}\),那就将这个值设为一个比 \(10^{18}\) 大的数即可,因为此时无论 \(v\) 取何值都不可能为 \(\operatorname{lcm}\) 的倍数。
总时间复杂度 \(\mathcal O(n\log n\log v)\)。
Code
int N, M;
i64 A[_N];
i64 lcm(i64 x, i64 y) {
i64 g = gcd(x, y);
long double tmp = x * 1. / g * y;
if (tmp > inf) return inf;
return x / g * y;
}
struct Segtree {
u32 val[_N<<2];
i64 tag[_N<<2];
#define LC (k << 1)
#define RC (k << 1 | 1)
#define mid ((l + r) >> 1)
void pushup(int k) {
val[k] = val[LC] + val[RC];
tag[k] = lcm(tag[LC], tag[RC]);
}
void build(int k, int l, int r) {
if (l == r) return val[k] = tag[k] = A[l], void();
build(LC, l, mid), build(RC, mid + 1, r), pushup(k);
}
void update(int k, int l, int r, int a, int b, i64 v) {
if (v % tag[k] == 0) return ;
if (l == r) return val[k] = tag[k] = gcd(tag[k], v), void();
if (a <= mid) update(LC, l, mid, a, b, v);
if (b > mid) update(RC, mid + 1, r, a, b, v);
pushup(k);
}
u32 query(int k, int l, int r, int a, int b) {
if (l >= a && r <= b) return val[k];
u32 res = 0;
if (a <= mid) res += query(LC, l, mid, a, b);
if (b > mid) res += query(RC, mid + 1, r, a, b);
return res;
}
} sgt;
void _() {
cin >> N >> M;
For(i, 1, N) cin >> A[i];
sgt.build(1, 1, N);
while (M--) {
int opt, l, r; i64 x;
cin >> opt >> l >> r;
if (opt == 1) {
cin >> x;
sgt.update(1, 1, N, l, r, x);
} else fmtout("{}\n", sgt.query(1, 1, N, l, r));
}
}