『做题记录』P9989 [Ynoi Easy Round 2023] TEST_69
累了,整道水的。(懒
[P9989 Ynoi Easy Round 2023] TEST_69
Description
给定一个长为 \(n\) 的序列 \(a\) ,有 \(m\) 次操作。
每次有两种操作:
1 l r x
:对于区间 \([l,r]\) 内所有 \(i\) ,将 \(a_i\) 变成 \(\gcd(a_i,x)\) 。
2 l r
:查询区间 \([l,r]\) 的和,答案对 \(2^{32}\) 取模后输出。
\(1\le a_i,x \le 10^{18},1\le n,l,r\le 2\times10^5,1\le m\le 5\times 10^5\) 。
Solution
首先对于操作一,我们不难发现无效的操作其实是非常多的。因为 \(a_i=\gcd(a_i, x)\) 这个操作如果使 \(a_i\) 发生变化,那么 \(a_i\) 是会变为至多 \(\left\lfloor\dfrac {a_i}2\right\rfloor\) 的,也就是说 \(a_i\) 最多变化 \(\log V\) 次,接下来就是考虑如何不去执行无用操作了。
考虑到当 \(\text{lcm}_{l\le i\le r}(a_i)|x\) 时,这一段区间都不会发生变化,因此我们只需要记录一下 \(\text{lcm}\) 即可判断是否修改。维护lcm的复杂度显然也是正确的,因为一个数lcm发生变化最多 \(\log V\) 次,而大于 \(V\) 的我们就可以赋成 inf 了,因为此时 \(x\) 一定不是它的倍数,也就是说修改的复杂度是取lcm的复杂度 \(\log V\) 乘上取lcm次数(线段树层数) \(\log n\) 。
总复杂度 \(O(n\log n\log V)\) 。
Code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define mp make_pair
#define vi vector<int>
#define eb emplace_back
#define pii pair<int, int>
#define fi first
#define se second
#define Rep(rp,a,b) for(int rp=a;rp<b;++rp)
#define rep(rp,a,b) for(int rp=a;rp<=b;++rp)
#define per(bl,a,b) for(int bl=a;bl>=b;--bl)
mt19937 rnd(114514);
#define segc int mid = L+R>>1, lc = now<<1, rc = lc|1
const int N = 2e5+5, INF = 1e9;
const LL MOD = 1ll<<32;
template <typename T> inline void chkmin(T &x,T y) {x = y>x ? x : y;}
template <typename T> inline void chkmax(T &x,T y) {x = y>x ? y : x;}
inline LL read() {
register LL x = 0, f = 1;
register char ch = 0;
while(ch < 48 || ch > 57) {
ch = getchar();
if (ch == '-') f = -1;
}
while(ch >= 48 && ch <= 57) x = x*10+(ch^48), ch = getchar();
return f*x;
}
LL a[N];
LL _lcm(LL x, LL y) {
if (x < 0 || y < 0 || (__int128)x/__gcd(x, y)*y > 1e18) return -1;
return x/__gcd(x, y)*y;
}
struct BLCK{
LL sum, lcm;
BLCK operator +(const BLCK &x) const {
return (BLCK){(sum+x.sum)%MOD, _lcm(lcm, x.lcm)};
}
}tr[N<<2];
void build(int now, int L, int R) {
if (L == R) {
tr[now].sum = tr[now].lcm = a[L];
return ;
}
segc;
build(lc, L, mid), build(rc, mid+1, R);
tr[now] = tr[lc]+tr[rc];
}
void update(int now, int L, int R, int ql, int qr, LL x) {
if (qr < L || R < ql) return;
if (tr[now].lcm > 0 && x%tr[now].lcm == 0) return;
if (L == R) {
tr[now].sum = tr[now].lcm = __gcd(tr[now].sum, x);
return;
}
segc;
update(lc, L, mid, ql, qr, x), update(rc, mid+1, R, ql, qr, x);
tr[now] = tr[lc]+tr[rc];
}
LL query(int now, int L, int R, int ql, int qr) {
if (qr < L || R < ql) return 0;
if (ql <= L && R <= qr) return tr[now].sum;
segc;
return (query(lc, L, mid, ql, qr)+query(rc, mid+1, R, ql, qr))%MOD;
}
int main() {
int n = read(), Q = read();
rep (i, 1, n) a[i] = read();
build(1, 1, n);
while (Q --) {
int opt = read(), ql = read(), qr = read();
if (opt == 1) {
LL x = read();
update(1, 1, n, ql, qr, x);
} else {
printf("%lld\n", query(1, 1, n, ql, qr));
}
}
return 0;
}
Summary
what can i say