9.9 另一个区间问题
Posted venividivici
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9.9 另一个区间问题相关的知识,希望对你有一定的参考价值。
题意
有一个长度为\(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-\frac1p_i) \ (p_i \in prime)
\]
维护\(n\)可以直接用维护区间乘法的线段树,由于\(n\)与质因子可以分开维护,此时就可以直接取模了
接下来的问题是维护质因子
我们惊喜的发现\(x\leq300\)!这说明最多可能只有\(62\)个质因子
一个朴素的想法是,对每个质因子开一颗线段树
每次查询\([L,R]\)这个区间中是否有该质因子
考虑进行优化,我们发现\(62\)与\(\ttlong \ long\)二进制下的范围很相近
我们进行压位,把\(62\)个质因子压成一个\(\ttlong \ 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;
以上是关于9.9 另一个区间问题的主要内容,如果未能解决你的问题,请参考以下文章
P5199 [USACO19JAN]Mountain View S(区间&排序)