标签:长度 tin var 线段树 一个 == inline 序列 for
有一个长度为\(n\)的正整数序列\(a\),可以进行\(m\)次操作,每次操作有两种类型:
\(op=1\)
修改操作,表示将区间\([L,R]\)内的所有数都乘上一个正整数\(x\)
\(op=2\)
询问操作,询问\([L,R]\)区间中所有数乘积的欧拉函数,即\(\varphi(\prod_{i=L}^R a_i)\)
答案取模\(mod=998244353\)
\(n,m\leq 4\times 10^5,x\leq 300\)
区间操作,想到用线段树维护
现在主要是求\(\varphi\)
众所周知,对于\(\varphi(n)\)我们有如下的计算公式
\[
\varphi(n)=n\prod_{p_i|n}(1-\frac{1}{p_i}) \ (p_i \in prime)
\]
维护\(n\)可以直接用维护区间乘法的线段树,由于\(n\)与质因子可以分开维护,此时就可以直接取模了
接下来的问题是维护质因子
我们惊喜的发现\(x\leq300\)!这说明最多可能只有\(62\)个质因子
一个朴素的想法是,对每个质因子开一颗线段树
每次查询\([L,R]\)这个区间中是否有该质因子
考虑进行优化,我们发现\(62\)与\(\tt{long \ long}\)二进制下的范围很相近
我们进行压位,把\(62\)个质因子压成一个\(\tt{long \ long }\)数
这样\(62\)颗线段树就被我们压成一颗线段树了,维护区间或即可
#include <cstdio>
using namespace std;
const int N = 1e6 + 10;
const int mod = 998244353;
int n, m;
int id[N], pri[N];
long long inv[N];
long long qpow(long long x, long long y) {
long long res = 1;
while (y) {
if (y & 1) res = res * x % mod;
x = x * x % mod, y >>= 1;
}
return res;
}
struct tree_mul {
#define ls x << 1
#define rs x << 1 | 1
long long val[N << 2], tag[N << 2];
void clear() { for (int i = 1; i <= (n << 2); ++i) tag[i] = val[i] = 1; }
void pushdown(int x, int l, int r) {
int mid = l + r >> 1;
if (tag[x] != 1) {
(tag[ls] *= tag[x]) %= mod, (tag[rs] *= tag[x]) %= mod;
(val[ls] *= qpow(tag[x], mid - l + 1)) %= mod, (val[rs] *= qpow(tag[x], r - mid)) %= mod;
tag[x] = 1;
}
}
void modify(int x, int l, int r, int ql, int qr, long long v) {
if (ql <= l && r <= qr) {
(val[x] *= qpow(v, r - l + 1)) %= mod, (tag[x] *= v) %= mod;
return;
}
int mid = l + r >> 1;
pushdown(x, l, r);
if (ql <= mid)
modify(ls, l, mid, ql, qr, v);
if (qr > mid)
modify(rs, mid + 1, r, ql, qr, v);
val[x] = val[ls] * val[rs] % mod;
}
long long query(int x, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr)
return val[x];
int mid = l + r >> 1;
long long res = 1;
pushdown(x, l, r);
if (ql <= mid)
(res *= query(ls, l, mid, ql, qr)) %= mod;
if (qr > mid)
(res *= query(rs, mid + 1, r, ql, qr)) %= mod;
return res;
}
#undef ls
#undef rs
} a;
struct tree_or {
#define ls x << 1
#define rs x << 1 | 1
long long val[N << 2], tag[N << 2];
void clear() { for (int i = 1; i <= (n << 2); ++i) val[i] = tag[i] = 0; }
void pushdown(int x) {
if (tag[x]) {
tag[ls] |= tag[x], tag[rs] |= tag[x];
val[ls] |= tag[x], val[rs] |= tag[x];
tag[x] = 0;
}
}
void modify(int x, int l, int r, int ql, int qr, long long v) {
if (ql <= l && r <= qr) {
val[x] |= v, tag[x] |= v;
return;
}
int mid = l + r >> 1;
pushdown(x);
if (ql <= mid)
modify(ls, l, mid, ql, qr, v);
if (qr > mid)
modify(rs, mid + 1, r, ql, qr, v);
val[x] = val[ls] | val[rs];
}
long long query(int x, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr)
return val[x];
int mid = l + r >> 1;
long long res = 0;
pushdown(x);
if (ql <= mid)
res |= query(ls, l, mid, ql, qr);
if (qr > mid)
res |= query(rs, mid + 1, r, ql, qr);
return res;
}
#undef ls
#undef rs
} b;
long long divide(long long x) {
long long res = 0;
for (int i = 2; i <= x; ++i) {
if (x % i) continue;
res |= (1LL << id[i]);
while (x % i == 0) x /= i;
}
return res;
}
void init() {
int tot = 0;
for (int i = 2; i <= 300; ++i) {
int ok = 1;
for (int j = 2; j * j <= i; ++j)
if (i % j == 0) ok = 0;
if (ok) id[i] = ++tot, pri[tot] = i;
}
for (int i = 1; i <= tot; ++i)
inv[i] = qpow(pri[i], mod - 2);
a.clear(), b.clear();
}
int main() {
scanf("%d%d", &n, &m);
init();
long long x;
for (int i = 1; i <= n; ++i) {
scanf("%lld", &x);
a.modify(1, 1, n, i, i, x);
b.modify(1, 1, n, i, i, divide(x));
}
int op, l, r;
while (m--) {
scanf("%d", &op);
if (op == 1) {
scanf("%d%d%lld", &l, &r, &x);
a.modify(1, 1, n, l, r, x);
b.modify(1, 1, n, l, r, divide(x));
}
if (op == 2) {
scanf("%d%d", &l, &r);
long long num = a.query(1, 1, n, l, r);
long long fac = b.query(1, 1, n, l, r);
for (int i = 1; i <= 62; ++i)
if (fac >> i & 1) num = (num - num * inv[i] % mod + mod) % mod;
printf("%lld\n", num);
}
}
return 0;
}
标签:长度 tin var 线段树 一个 == inline 序列 for
原文地址:https://www.cnblogs.com/VeniVidiVici/p/11509272.html