Sample Input
5 5
1 2 3 4 5
1 1 5
0 1 3 2
1 1 5
0 2 5 6
1 1 5
Sample Output
10
11
37
题意:
操作一:区间
[
l
,
r
]
[l,r]
[l,r] 乘
w
w
w,操作二:查询
∑
i
=
l
i
=
r
φ
(
a
[
i
]
)
\sum_{i=l}^{i=r}\varphi(a[i])
∑i=li=rφ(a[i])
φ
(
x
)
\varphi(x)
φ(x) 为
x
x
x 的欧拉函数
思路:
考虑到区间查询是求每个数欧拉函数的和,因此我们要去维护区间欧拉函数的和
欧拉函数特殊的性质:
设
m
m
m 为质数
- 如果 n % m ! = 0 n \ \% \ m!=0 n % m!=0 互质,那么 φ ( n ∗ m ) = φ ( n ) ∗ φ ( m ) = φ ( n ) ∗ ( m − 1 ) \varphi(n*m)=\varphi(n)*\varphi(m)=\varphi(n)*(m-1) φ(n∗m)=φ(n)∗φ(m)=φ(n)∗(m−1)
- 如果 n % m = 0 n \ \% \ m=0 n % m=0,那么 φ ( n ∗ m ) = φ ( n ) ∗ m \varphi(n*m)=\varphi(n)*m φ(n∗m)=φ(n)∗m
根据唯一分解定理:
n
=
p
1
q
1
p
2
q
2
p
3
q
3
.
.
.
p
n
q
n
n=p_1^{q_1}p_2^{q_2}p_3^{q_3}...p_n^{q_n}
n=p1q1p2q2p3q3...pnqn
可以得到
φ
(
x
)
=
φ
(
p
1
q
1
)
φ
(
p
2
q
2
)
φ
(
p
3
q
3
)
.
.
.
φ
(
p
n
q
n
)
\varphi(x)=\varphi(p_1^{q_1})\varphi(p_2^{q_2})\varphi(p_3^{q_3})...\varphi(p_n^{q_n})
φ(x)=φ(p1q1)φ(p2q2)φ(p3q3)...φ(pnqn)
由于我们每次乘的
w
w
w 都在
1
−
100
1-100
1−100 之内,我们可以直接预处理出每个数含有的质因子的情况
然后每个数乘以
p
1
q
1
p_1^{q_1}
p1q1 即可。
但是我们还要讨论每个数乘
p
1
q
1
p_1^{q_1}
p1q1 ,该如何维护每个数的欧拉函数值
- 如果区间内每个数都不含质因子 p 1 p_1 p1,那么每个数乘 p 1 q 1 p_1^{q_1} p1q1 次方,相当于每个数的欧拉函数值乘以 φ ( p 1 q 1 ) \varphi(p_1^{q_1}) φ(p1q1)
- 如果区间内每个数都含有质因子 p 1 p_1 p1,考虑到每个数的 p 1 p_1 p1质因子次方参差不齐,我们先一个一个乘。每个数乘 p 1 p_1 p1 相当于 每个数的欧拉函数值也乘 p 1 p_1 p1。显然,乘 q 1 q_1 q1 次 p 1 p_1 p1 与 直接乘以 p 1 q 1 p_1^{q_1} p1q1 是等价的。
- 都不符合就暴力递归
每个节点开一个长度为
25
25
25 的
b
i
t
s
e
t
bitset
bitset 表示区间内是否所有数都含有这个质因子。
当整个区间的数都含有质因子
p
p
p 时,可以直接打上一个乘法标记,没有则向下递归。
由于只有
25
25
25 个质数,所以每个节点至多被暴力修改
25
25
25 次。
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 9;
const int maxm = 109;
const int mod = 998244353;
bitset <maxm> vis;// 标记是否为质因子
int eul[maxm], p[maxm], id[maxm];// 质因子 i 为第 id[i]+1 个质因子(从2开始
vector <pair<int,int> > val[maxm];// v[i]存 i 的所以质因子的最高次方及质因子的 id
bitset<26> G[maxm];// G[i][j] 代表 i 能否被 j 整除
void init()
{
eul[1] = 1;
for(int i = 2; i <= 100; ++i)
{
if(!vis[i]) p[++p[0]] = i, eul[i] = i - 1, val[i] = {{i, id[i] = p[0] - 1}}, G[i][p[0] - 1] = 1;
for(int j = 1, x; j <= p[0] && (x = i * p[j]) <= 100; ++j)
{
vis[x].flip(), val[x] = val[i], G[x] = G[i];
if(i % p[j]) eul[x] = eul[i] * (p[j] - 1), val[x].emplace_back(p[j], id[p[j]]), G[x][id[p[j]]] = 1;
else
{
eul[x] = eul[i] * p[j];
for(auto &w : val[x]) if(w.second == id[p[j]])
w.first *= p[j];
break;
}
}
}
}
int n, m;
int a[maxn];
struct node
{
int mul, sum;
bitset <26> f;
}tr[maxn << 2];
#define lc (p << 1)
#define rc (p << 1 | 1)
#define endl '\n'
void pushup(int p)
{
tr[p].sum = (tr[lc].sum + tr[rc].sum) % mod;
tr[p].f = tr[lc].f & tr[rc].f;
}
void pushdown(int p){
if(tr[p].mul > 1)
{
tr[lc].sum = 1ll * tr[lc].sum * tr[p].mul % mod;
tr[lc].mul = 1ll * tr[lc].mul * tr[p].mul % mod;
tr[rc].sum = 1ll * tr[rc].sum * tr[p].mul % mod;
tr[rc].mul = 1ll * tr[rc].mul * tr[p].mul % mod;
tr[p].mul = 1;
}
}
void build(int p = 1, int cl = 1, int cr = n)
{
tr[p].mul = 1;
if(cl == cr){
tr[p].f = G[a[cl]], tr[p].sum = eul[a[cl]];return;
}
int mid = (cl + cr) >> 1;
build(lc, cl, mid);
build(rc, mid + 1, cr);
pushup(p);
}
void update(int l, int r, int x, int i, int p = 1, int cl = 1, int cr = n)
{
if(cl >= l && cr <= r && tr[p].f[i]){
tr[p].mul = 1ll * tr[p].mul * x % mod;
tr[p].sum = 1ll * tr[p].sum * x % mod;
return;
}
if(cl == cr){
tr[p].f[i] = 1, tr[p].sum = 1ll * tr[p].sum * eul[x] % mod; return;
}
int mid = (cl + cr) >> 1;
pushdown(p);
if(mid >= l) update(l, r, x, i, lc, cl, mid);
if(mid < r) update(l, r, x, i, rc, mid + 1, cr);
pushup(p);
}
int query(int l, int r, int p = 1, int cl = 1, int cr = n)
{
if(cl >= l && cr <= r) return tr[p].sum;
int mid = (cl + cr) >> 1, ans = 0;
pushdown(p);
if(mid >= l) ans = query(l, r, lc, cl, mid) % mod;
if(mid < r) ans = (ans + query(l, r, rc, mid + 1, cr)) % mod;
return ans;
}
void work()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i) cin >> a[i];
build();
while(m--)
{
int op, l, r;
cin >> op >> l >> r;
if(!op){
int w;cin >> w;
for(auto x : val[w])
update(l, r, x.first, x.second);
}
else cout << query(l, r) << endl;
}
}
int main()
{
ios::sync_with_stdio(0);
init();
work();
return 0;
}