2021ICPC网络赛 - L. Euler Function

题目概览

image-20210927164841332

思路

考场上作为队伍$ds$选手,写这个题时候把队友演了,虽然知道是处理$25$个线段树,但是一直没调出来(

考后补题,整理如下。

前置知识:image-20210927170114944


首先观察到最开始时候$1 \leq x_i \leq 100$​,而且$100$​以内素数只有$25$​​个,考虑这是一个突破点。

假设说要对区间$[L,R]$​​内所有数都乘上一个数字$W$,分两种情况:

  • 假设数字为$W=p_1^{k_1} \times p_2^{k_2} \times \dots p_l^{k_l}$,且区间$[L,R]$内所有数均含有质因子$p_1,p_2,\dots,p_l$​时​

    我们考虑分开算每个质因子的贡献:

    如:$[2 \times 2 \times 3,2 \times 3,2 \times 3 \times 3]$​,即$[12,6,18]$​,我们可以拆为:

    • $p=2:[\phi(4)=2, \phi(2)=1, \phi(2)=1]$
    • $p=3:[\phi(3)=2,\phi(3)=2, \phi(9)=6]$​

    由于任意两个质因数间一定互质,且$gcd(n,m)=1$时,$\phi(nm)=\phi(n)\phi(m)$,原数组的欧拉函数值的和为各个质因子对应的欧拉函数的积。

    即:$[\phi(12)=\phi(4) \times \phi(3), \phi(6)=\phi(2) \times \phi(3), \phi(18)=\phi(2) \times \phi(9)]$​

    又因为区间$[L,R]$​内均含有所乘的数的质因子$p_1,p_2,\dots,p_l$​,且当$n=p^k$​时,$\phi(n)=(p-1)p^{k-1}$​,那么在计算区间乘的贡献时,其实只要对$[L,R]$​的区间和乘上一个$W$​就可以了,用上面的数组$[12,6,18]$​​举例如下:

    假设$W=6=2 \times 3$​​,结果数组为$[12 \times 6,6 \times 6,18 \times 6]$,即:$[72,36,108]$,对应的欧拉函数为$[24,12,36]$;

    拆开来看,有:

    • $p=2:[\phi(4 \times 2) = \phi(4) \times 2 = 4, \phi(2 \times 2) = \phi(2) \times 2 = 2, \phi(2 \times 2)=\phi(2) \times 2 = 2]$​
    • $p=3:[\phi(3 \times 3)=\phi(3) \times 3=6,\phi(3 \times 3)=\phi(3) \times 3=6, \phi(9 \times 3)=\phi(9) \times 3=18]$​

    即$[\phi(8)\times\phi(9)=24,\phi(4)\times\phi(9)=12,\phi(4)\times\phi(27)=3]$​

    也就是说,这种情况下,你只需要对整个区间的和乘上$W$,得到的就是最终答案。

  • 如果区间$[L,R]$内存在数字并不都含有$W$的全部质因数呢?

    只要对区间内这些并不含有$W$的全部质因数的数字暴力更新就可以了。

    由于$1\leq x_i,w_i \leq 100$,质因数一定在$100$以内,也就是说最多只有$25$个质因子。

    考虑最坏情况,$25$个质因子暴力更新,所需要的也只是$25n$​次操作而已,之后只要用线段树维护区间乘和区间和就可以了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/* vegetable1024 | Maybe Lovely? */

#include <bits/stdc++.h>
#define maxn 800005
#define int long long
#define lson(x) (x << 1)
#define rson(x) (x << 1 | 1)
using namespace std;

//DEBUG TEMPLATE
template<typename T>
void Print(T value){
std::cout << value << '\n';
}
template<typename Head, typename... Rail>
void Print(Head head, Rail... rail){
std::cout << head << ", ";
Print(rail...);
}

int read(){
int x = 0; char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x;
}

int qpow(int a, int b, int m){
if(b == 0) return 1;
int tmp = qpow(a, b / 2, m);
tmp = (tmp * tmp) % m;
if(b & 1) tmp = (tmp * a) % m;
return tmp;
}

const int mod = 998244353;
//维护区间欧拉函数的和,支持区间乘法
struct SegTreeSum{
struct Node{
int lf, rt, sum, lazy;
} t[maxn];
void Pushdown(int now){
if(t[now].lazy > 1){
t[lson(now)].lazy = (t[now].lazy * t[lson(now)].lazy) % mod;
t[rson(now)].lazy = (t[now].lazy * t[rson(now)].lazy) % mod;
t[lson(now)].sum = (t[now].lazy * t[lson(now)].sum) % mod;
t[rson(now)].sum = (t[now].lazy * t[rson(now)].sum) % mod;
t[now].lazy = 1;
}
}
void Pushup(int now){
t[now].sum = (t[lson(now)].sum + t[rson(now)].sum) % mod;
}
void Build(int now, int lf, int rt){
t[now].lf = lf;
t[now].rt = rt;
if(lf == rt){
t[now].sum = t[now].lazy = 1;
return;
}
t[now].lazy = 1;
int mid = (lf + rt) / 2;
Build(lson(now), lf, mid);
Build(rson(now), mid + 1, rt);
Pushup(now);
}
void Update(int now, int lf, int rt, int k){
if(t[now].lf >= lf && t[now].rt <= rt){
t[now].sum = (t[now].sum * k) % mod;
t[now].lazy = (t[now].lazy * k) % mod;
return;
}
if(t[now].lf > rt || t[now].rt < lf){
return;
}
Pushdown(now);
Update(lson(now), lf, rt, k);
Update(rson(now), lf, rt, k);
Pushup(now);
}
int Query(int now, int lf, int rt){
if(t[now].lf >= lf && t[now].rt <= rt){
return t[now].sum;
}
if(t[now].lf > rt || t[now].rt < lf){
return 0;
}
Pushdown(now);
return (Query(lson(now), lf, rt) + Query(rson(now), lf, rt)) % mod;
}
} smt;
//计算每个质因子的出现次数最大值和最小值,判断是否要暴力更新
struct SegTreeRM{
struct Node{
int lf, rt, minn, maxx, lazy;
} t[maxn];
void Pushup(int now){
t[now].maxx = max(t[lson(now)].maxx, t[rson(now)].maxx);
t[now].minn = min(t[lson(now)].minn, t[rson(now)].minn);
}
void Pushdown(int now){
if(t[now].lazy){
t[lson(now)].lazy += t[now].lazy; t[rson(now)].lazy += t[now].lazy;
t[lson(now)].maxx += t[now].lazy; t[rson(now)].maxx += t[now].lazy;
t[lson(now)].minn += t[now].lazy; t[rson(now)].minn += t[now].lazy;
t[now].lazy = 0;
}
}
void Build(int now, int lf, int rt){
t[now].lf = lf;
t[now].rt = rt;
if(lf == rt){
t[now].maxx = t[now].minn = t[now].lazy = 0;
return;
}
int mid = (lf + rt) / 2;
Build(lson(now), lf, mid);
Build(rson(now), mid + 1, rt);
Pushup(now);
}
void Update(int now, int lf, int rt, int p, int c){
if(t[now].lf >= lf && t[now].rt <= rt){
if(t[now].maxx == 0){
t[now].maxx += c; t[now].minn += c; t[now].lazy += c;
smt.Update(1, t[now].lf, t[now].rt, ((p - 1) * qpow(p, c - 1, mod)) % mod);
}
else if(t[now].minn > 0){
t[now].maxx += c; t[now].minn += c; t[now].lazy += c;
smt.Update(1, t[now].lf, t[now].rt, qpow(p, c, mod));
}
else{
Pushdown(now);
Update(lson(now), lf, rt, p, c);
Update(rson(now), lf, rt, p, c);
Pushup(now);
}
return;
}
if(t[now].lf > rt || t[now].rt < lf){
return;
}
Pushdown(now);
Update(lson(now), lf, rt, p, c);
Update(rson(now), lf, rt, p, c);
Pushup(now);
}
} tr[30];

int n, m, xi[maxn];
bool check(int val){
if(val == 1) return false;
for(int i = 2; i * i <= val; i++)
if(val % i == 0) return false;
return true;
}
int cnt, pri[maxn];
void initp(int lim){
for(int i = 1; i <= lim; i++)
if(check(i)) pri[++cnt] = i;
smt.Build(1, 1, n);
for(int i = 1; i <= cnt; i++) tr[i].Build(1, 1, n);
}
void De(int l, int r, int w){
for(int i = 1; i <= cnt && w > 1; i++){
int p = pri[i], c = 0;
while(w % pri[i] == 0){
w /= pri[i];
c++;
}
if(c) tr[i].Update(1, l, r, p, c);
}
}
signed main(void)
{
n = read(); m = read(); initp(100);
for(int i = 1; i <= n; i++) xi[i] = read();
for(int i = 1; i <= n; i++) De(i, i, xi[i]);
for(int i = 1; i <= m; i++)
{
int ty = read();
if(ty == 0){
int l = read(), r = read(), w = read();
De(l, r, w);
}
else{
int l = read(), r = read();
printf("%lld\n", smt.Query(1, l, r));
}
}
return 0;
}
如果你觉得还不错,就请咕咕的作者喝杯咖啡吧QAQ