CF1732E

算法复健ing

原题链接:https://codeforces.com/contest/1732/problem/E

大意:给定长度为n的数组a与b,有两种操作,为a区间赋值x,区间最值查询,需要维护的是a[i]*b[i]/gcd(a[i],b[i])/gcd(a[i],b[i])的最值

题解:

先从暴力入手,暴力修改,暴力询问,时间复杂度为O(qN),时间复杂度为O(3e9*log)

但注意,区间赋值时,赋的值都是相同的,故容易联想到分块操作,离散块暴力修改,区间块使用tag修改

假设块长为k,则我们获得了n/k个块,接下来考虑如何维护对各个块的询问

对于离散块,暴力修改,并记录修改后的值是否为最值

由于能赋的值范围较小,对于整块,我们对每个块维护一个ans[k],为当前块都被赋成k值时,当前块的答案.

对于给定的k,我们遍历k的所有约数,对于所有约数,寻找最小的b[i],能被该约数整除(即先假定该约数是gcd,当gcd一定时,b[i]越小,其代表的答案越小),遍历所有的约数后,记录最小答案的位置为i

证明该最小答案的正确性:假设k的两个约数d1,d2均能整除某个b[i],则其中较大者一定是更优答案,且一定会被遍历到.即最终取得的一定是最优情况

这里写法有个小trick,直接for(int i=1;i<=MAX;i++),约数放在外面,然后for(int k=i;k<=MAX;k+=i),复杂度为MAX(logMAX);同时用个vis数组标记当前块中是否存在b[i]=k,若存在,则直接取符合条件的最小的b[i]去更新答案

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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#include <bits/stdc++.h>
using namespace std;
using ll = int64_t;
/*head*/

#define fi first
#define se second
#define rep(i, a, b) for (ll i = (a); i < (b); i++)
#define sz(x) ((ll)x.size())
//#define DE_BUG
/*define*/
const ll maxn = 5e4 + 10;
const ll max2 = 3e2 + 10;
const ll INF = 1e18;
ll T, n, m, k, q, sq;
ll L[max2], R[max2];
ll mn[max2];
ll lazy[max2];
ll id[maxn];
ll p[max2][maxn];
bool vis[maxn];
ll a[maxn], b[maxn];
inline ll read()
{
ll s = 0;
char ch = getchar();
while (ch < '0' || ch > '9')
{
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar();
}
return s;
}

inline void write(ll x)
{

if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
ll gcd(ll a, ll b)
{
ll A = __builtin_ctz(a), B = __builtin_ctz(b), k = min(A, B);
b >>= B;
while (a)
{
a >>= A;
ll c = b - a;
A = __builtin_ctz(c);
b = min(a, b), a = abs(c);
}
return b << k;
}
void init(ll n)
{
sq = sqrt(n);
R[0] = 0;
for (ll i = 1; i <= sq; i++)
{
L[i] = R[i - 1] + 1;
R[i] = i * sq;
for (ll j = L[i]; j <= R[i]; j++)
{
id[j] = i;
}
}
if (R[sq] < n)
{
sq++;
R[sq] = n, L[sq] = R[sq - 1] + 1;
for (ll j = L[sq]; j <= R[sq]; j++)
{
id[j] = sq;
}
}
}
void pre()
{
for (ll i = 1; i <= sq; i++)
{
lazy[i] = 0;
mn[i] = INF;
for (ll j = 1; j < maxn; j++)
{
p[i][j] = INF;
}
}
for (ll i = 1, tmp, d; i <= n; i++)
{
d = gcd(a[i], b[i]);
tmp = a[i] / d * b[i] / d;
mn[id[i]] = min(mn[id[i]], tmp);
}
for (ll i = 1; i <= sq; i++)
{
for (ll j = L[i]; j <= R[i]; j++)
{
vis[b[j]] = 1;
}
for (ll j = 1, tmp; j < maxn; j++)
{
tmp = 0; //找到最小的b[i]
for (ll k = j; k < maxn; k += j)
{
if (vis[k])
{
tmp = k;
break;
}
}
if (!tmp)
{
continue;
}
for (ll k = j; k < maxn; k += j)
{
p[i][k] = min(p[i][k], k / j * tmp / j);
}
}
for (ll j = L[i]; j <= R[i]; j++)
{
vis[b[j]] = 0;
}
}
}
void up(ll u)
{
mn[u] = INF;
for (ll i = L[u], tmp, d; i <= R[u]; i++)
{
d = gcd(a[i], b[i]);
tmp = a[i] / d * b[i] / d;
mn[u] = min(mn[u], tmp);
}
}
void update(ll l, ll r, ll val)
{
ll u = id[l], v = id[r];
if (u == v)
{
if (lazy[u])
{
for (ll i = L[u]; i <= R[u]; i++)
{
if (i >= l && i <= r)
{
a[i] = val;
}
else
{
a[i] = lazy[u];
}
}
lazy[u] = 0;
}
else
{
for (ll i = l; i <= r; i++)
{
a[i] = val;
}
}
up(u);
return;
}
else
{
if (lazy[v])
{
for (ll i = L[v]; i <= R[v]; i++)
{
if (i >= l && i <= r)
{
a[i] = val;
}
else
{
a[i] = lazy[v];
}
}
lazy[v] = 0;
}
else
{
for (ll i = L[v]; i <= r; i++)
{
a[i] = val;
}
}
if (lazy[u])
{
for (ll i = L[u]; i <= R[u]; i++)
{
if (i >= l && i <= r)
{
a[i] = val;
}
else
{
a[i] = lazy[u];
}
}
lazy[u] = 0;
}
else
{
for (ll i = l; i <= R[u]; i++)
{
a[i] = val;
}
}
up(u), up(v);
for (ll i = u + 1; i < v; i++)
{
lazy[i] = val;
mn[i] = p[i][val];
}
}
}
ll query(ll l, ll r)
{
ll u = id[l], v = id[r];
ll ret = INF;
if (u == v)
{
for (ll i = l, tmp, d; i <= r; i++)
{
if (lazy[u])
{
a[i] = lazy[u];
}
d = gcd(a[i], b[i]);
tmp = a[i] / d * b[i] / d;
ret = min(tmp, ret);
}
return ret;
}
else
{
for (ll i = l, tmp, d; i <= R[u]; i++)
{
if (lazy[u])
{
a[i] = lazy[u];
}
d = gcd(a[i], b[i]);
tmp = a[i] / d * b[i] / d;
ret = min(tmp, ret);
}
for (ll i = L[v], tmp, d; i <= r; i++)
{
if (lazy[v])
{
a[i] = lazy[v];
}
d = gcd(a[i], b[i]);
tmp = a[i] / d * b[i] / d;
ret = min(tmp, ret);
}
for (ll i = u + 1; i < v; i++)
{
ret = min(ret, mn[i]);
}
return ret;
}
}
void solve()
{
cin >> n >> q;
for (ll i = 1; i <= n; i++)
{
cin >> a[i];
}
for (ll i = 1; i <= n; i++)
{
cin >> b[i];
}
init(n);
pre();
for (ll i = 1, t, l, r, x; i <= q; i++)
{
cin >> t >> l >> r;
if (t == 1)
{
cin >> x;
update(l, r, x);
}
else
{
cout << query(l, r) << "\n";
}
}
}

int main()
{
T = 1;
cin.tie(0);
ios::sync_with_stdio(false);
// cin >> T;
while (T--)
{
solve();
}
return 0;
}

注意:用了nb版本的gcd,否则狂T

image-20221029001916613

两种gcd时间对比