CF710D Two Arithmetic Progressions 题解

CF710D Two Arithmetic Progressions

根号分治薄纱数论

看日报学习的根号分治:暴力美学——浅谈根号分治 - paulzrm 的博客

开始想学ODT的映射思想的推广 - 金珂拉 的博客,结果先学了 ODT,又学了根号分治,才搞懂前置知识。

  1. 什么是根号分治

    根号分治,是暴力美学的集大成体现。与其说是一种算法,我们不如称它为一个常用的 trick。

    其实就是将两个极端的暴力算法融合,根据不同数据选择更优的暴力,即可保证复杂度始终在可接受范围内。

  2. 如何使用根号分治

    即写出两个不同暴力算法(通常是在小范围内很快的和在大范围内很快的),之后确定一个阈值,在不同范围选择不同暴力。

大概根号分治就是这样,具体请看暴力美学——浅谈根号分治 - paulzrm 的博客

在这道题中,我们需要思考两种方法(定 $t$ 为阈值,默认 $a_1\ge a_2$,默认 $l=\max(l,\max(b_1,b_2))$):

  1. $a_1\le t$,则 $a_2\le t$。以 $\text{lcm}(a_1,a_2)$ 为一个循环节,显然的,一个循环节内至多有一个数同时出现在两个数列中。因此暴力枚举 $[\ l,\min(l+\text{lcm},r)\ ]$ 中的每一个数,若有 $i$ 使得 $i\in \text{数列1}\ \text{and}\ i \in \text{数列2}$,则可知共有 $\frac{(r-i)}{\text{lcm}+1}$ 个满足条件的值;反之没有所求值,输出 0

  2. $a_1\gt t$。则可以暴力枚举 $\text{数列1}$ 中的每一个处于 $[\ l,r\ ]$ 的数,若它同时处于 $\text{数列2}$ 则 ans++

之后,我们进行复杂度分析:

  1. 对于暴力1:时间复杂度为 $O(\text{lcm}(a_1,a_2))$。
  2. 对于暴力2:时间复杂度为 $O(\frac{r-l+1}{a_1})$。

以下内容不保证正确性(数学太差了):

故期望复杂度 $O(\text{lcm}(a_1,a_2)+(\frac{r-l+1}{a_1}))$,即 $O((t\times a_2)+(\frac{2e9}{t}))$,对于 $a_2$ 有限制 $a_2\le t$,使 $a_2=t$,则可算出阈值 $t=\sqrt[3]{2e9}$。约 $1259$。

对于阈值的计算并不准确,如果有更优方法欢迎指正。

代码:

//pre
const int t=1259;
signed main() {
	LL ans=0;
	LL a1,b1,a2,b2,l,r;
	read(a1,b1,a2,b2,l,r);
	if(b1>l) l=b1;
	if(b2>l) l=b2;	//保证l的大小为 b_1,b_2,l 中的最大值
	if(a2>a1) swap(a1,a2),swap(b1,b2); //保证 a_1>a_2
	LL lcm=a2*a1/__gcd(a1,a2);	//计算lcm  (lcm(a,b)*gcd(a,b)=a*b)
	if(a1<t) { 	//a_1 小于阈值
		for(int i=l;i<=min(l+lcm,r);i++) {
			if((i-b1)%a1==(i-b2)%a2&&(i-b2)%a2==0) {	//若满足题意
				ans=(r-i)/lcm+1;	//剩下有几个循环节并加上当前位置的一个
				break;
			}
		}
	}
	else {
		LL x;
		if(b1>=l) x=b1;
		else x=b1+ceil(1.00*(l-b1)/a1)*a1;//确定从哪里开始枚举(起始点要属于数列1)
		for(;x<=r;x+=a1) if((x-b2)%a2==0&&(x-b2)/a2>=0) ++ans;	//满足题意则ans++
	}
	write(ans); //输出
	return 0;	//好习惯捏
}

pre 前的内容为头文件与快读快写,如果需要戳这里

posted @ 2024-03-23 20:11  CheemsaDoge  阅读(2)  评论(0编辑  收藏  举报