CF1732E - Location

发布时间 2023-12-07 20:47:09作者: Imcaigou

警告&题外话

赛时看都没看这道题,赛后看感觉还行。

虽然这题我两个小时写不完,TLE十几次

此题偏难,代码难度较大(对于我的方法),建议评黑,不建议没做完 数列分块入门九道 的人做,因为不会讲分块基本操作。

如果有更好方法的不要嘲讽我。

如果发现我方法正确性与时空复杂度有误的请私聊。(免得丢脸)


题意

见翻译。


思路

分析操作的可实现性和数据范围,凭借做题经验,我们可以初步估计此题采用分块是最合适的。

Codeforces 评论区中也有更快的做法,好像是线段树,我也想讲,可惜我不会。

如何分块?我们会发现根据题目要求,对于分块中整体处理的块,我们必须 \(O(1)\) 解决,但修改操作是区间赋值,怎么办?

$ 1 \leq x,a_i,b_i \leq 5 \cdot 10^4 $

实际上,此题的值域全是 \(5\times 10^4\),设 \(W\) 表示 \(a_i\) 的最大取值,则遍历全体整数可能值与所有块的总时间复杂度可表示为 \(O(W \cdot \sqrt{n})\),而不会超时。

这个条件给了我们启发——如果可以将每个块中整体赋给 \(a_i\) 每种可能值时整个块查询的最小值预处理出来,那么就可以对分块中整体处理的块 \(O(1)\) 解决。

这又如何做?

首先,此题求:

\(\min\limits_{i \in [l,r]}\frac{\operatorname{lcm}(a_i,b_i)}{\gcd(a_i,b_i)}\)

我们会发现把分母从 \(\gcd(a_i,b_i)\) 换成 \(a_i,b_i\) 的任何一个公因数然后取 \(\min\),那么最小的结果仍然是 \(\frac{\operatorname{lcm}(a_i,b_i)}{\gcd(a_i,b_i)}\)

所以我们尝试先走弯路,考虑枚举一个块中所有 \(b_i\) 的所有因子,将其存入一个大小为 \(5 \times 10 ^ 4\) 的有序数对桶中(因子为下标,(\(b_i/\) 因子,因子)成为有序数对作为其中值),然后用类似线性筛的思路向上找倍数对其关于自身的有序数对取 \(\min\) 更新桶中当前值,大小比较的方式为:

对于两个有序数对 \((A,B)(C,D)\)

若满足:\(\frac{A}{B} \leq \frac{C}{D}\)

即:\(A \cdot D \leq B \cdot C\)

则称 \((A,B) \leq (C,D)\)

认真思考,对于一个更新完毕的有序数对桶:

\(ton_{first/second}\)

计算对于一个可能的整数 \(i\) 赋值给整个块的 \(a_i\),那么此时整个块查询的最大值即为:

\(\frac{i \cdot ton_{i_{first}}}{ton_{i_{second}}}\)

由此,我们将每个块中整体赋给 \(a_i\) 每种可能值时整个块查询的最大值预处理出来了,设 \(W\) 表示 \(a_i\) 最大取值,则单块时间复杂度 \(O(W + \log_{2}{n} \cdot \sqrt{n})\)

所以预处理总时间复杂度 \(O(W \cdot \sqrt{n} + n \cdot \log_{2}{n})\),剩下的就是分块基本操作。

(推销博客中)

此题常数较大,建议加:

火车头,超级快读,快输,以及其他卡常技艺。


代码

//火车头略
#include <bits/stdc++.h>
#include <immintrin.h>
using namespace std;
#define int unsigned int
int n, Q, a[50005], b[50005], Typ, L, R, X, prime[50005], cnt;
int sq, len, block[50005], bl[505], br[505], bmin[505], bcg[505], bres[505][50005], pl;
long long minn[50005], tlp[50005];
bool ton[50005], not_prime[50005];
vector < int > fac[50005];
static char buf[1000000], * p1 = buf, * p2 = buf;
#define getchar() p1 == p2 && (p2 = (p1 = buf) + fread (buf, 1, 1000000, stdin), p1 == p2) ? EOF : * p1 ++
inline int read()
{
	register int x = 0;
	register char c = getchar ();
	while (c < '0' || c > '9') c = getchar ();
	while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48),c = getchar ();
	return x;
}
void inline print (register int a){
	if (a < 0){
		putchar ('-');
		a = - a;
	}
	if (a < 10)
		putchar (a ^ 48);
	else {
		print (a / 10);
		putchar ((a % 10) ^ 48);
	}
}
inline int gcd (register int i, register int j){
	if (j == 0)
		return i;
	return gcd (j, i % j);
}
void inline push_d (register int i){
	if (bcg[i]){
		for (register int j = bl[i];j <= br[i];++ j)
			a[j] = bcg[i];
		bcg[i] = 0;
	}
}
void inline push_u (register int i){
	bmin[i] = INT_MAX;
	for (register int j = bl[i];j <= br[i];++ j){
		pl = gcd (a[j], b[j]);
		bmin[i] = min (bmin[i], a[j] / pl * b[j] / pl);
	}
}
void inline Build (){
	len = 100;
	sq = n / len;
	while (sq * len < n)
		++ sq;
	for (register int i = 1;i <= sq;++ i){
		for (register int j = 1;j <= 5e4;++ j){
			minn[j] = 5e9;
			tlp[j] = j;
			ton[j] = false;
		}
		bl[i] = br[i - 1] + 1;
		br[i] = min (n, bl[i] + len - 1);
		push_u (i);
		for (register int j = bl[i];j <= br[i];++ j){
			block[j] = i;
			if (! ton[b[j]]){
				ton[b[j]] = true;
				for (register int k = 0;k < (int) fac[b[j]].size ();++ k)
					minn[fac[b[j]][k]] = min (minn[fac[b[j]][k]], 1ll * b[j] / fac[b[j]][k]);
			}
		}
		for (register int j = 1;j <= 5e4;++ j){
			bres[i][j] = j / tlp[j] * minn[j];
			for (register int k = 1;k <= cnt && j * prime[k] <= 5e4;++ k)
				if (1ll * minn[j * prime[k]] * tlp[j] > 1ll * minn[j] * tlp[j * prime[k]]){
					minn[j * prime[k]] = minn[j];
					tlp[j * prime[k]] = tlp[j];
				}
		}
	}
}
void inline Update (register int l, register int r, register int x){
	push_d (block[l]);
	for (register int i = l;i <= min (br[block[l]], r);++ i)
		a[i] = x;
	push_u (block[l]);
	for (register int i = block[l] + 1;i < block[r];++ i){
		bcg[i] = x;
		bmin[i] = bres[i][x];
	}
	if (block[l] == block[r])
		return ;
	push_d (block[r]);
	for (register int i = bl[block[r]];i <= r;++ i)
		a[i] = x;
	push_u (block[r]);
}
inline int Query (register int l, register int r){
	register int Ans = INT_MAX * 2ll - 1;
	push_d (block[l]);
	for (register int i = l;i <= min (br[block[l]], r);++ i){
		pl = gcd (a[i], b[i]);
		Ans = min (Ans, a[i] / pl * b[i] / pl);
	}
	for (register int i = block[l] + 1;i < block[r];++ i)
		Ans = min (Ans, bmin[i]);
	if (block[l] == block[r])
		return Ans;
	push_d (block[r]);
	for (register int i = bl[block[r]];i <= r;++ i){
		pl = gcd (a[i], b[i]);
		Ans = min (Ans, a[i] / pl * b[i] / pl);
	}
	return Ans;
}
signed main (){
	for (register int i = 1;i <= 5e4;++ i){
		if (! not_prime[i])
			prime[++ cnt] = i;
		for (register int j = 1;i * j <= 5e4;++ j){
			if (i != 1)
				not_prime[i * j] = true;
			fac[i * j].push_back (i);
		}
	}
	n = read ();
	Q = read ();
	for (register int i = 1;i <= n;++ i)
		a[i] = read ();
	for (register int i = 1;i <= n;++ i)
		b[i] = read ();
	Build ();
	while (Q --){
		Typ = read ();
		L = read ();
		R = read ();
		if (Typ == 1){
			X = read ();
			Update (L, R, X);
		}
		if (Typ == 2){
			print (Query (L, R));
			putchar ('\n');
		}
	}
	return 0;
}