CF1673D. Lost Arithmetic Progression 思维

这篇博客探讨了一个数学问题:给定两个等差数列B和C,如何找出所有可能的等差数列A,使得C是A的子序列。博主详细分析了问题的边界情况,提出了判断是否存在满足条件的A的必要条件,并给出了计算方案数的思路。通过寻找B和C公差的公约数和最大公约数,博主建立了计算方案数的公式,并用C++实现了一个解决方案。文章强调了正确处理特殊情况的重要性,并提供了代码示例进行解释。
摘要由CSDN通过智能技术生成

link
思维

题意

设两个等差数列A,B公共序列为C,
现在给你两个等差数列B,C(输入两行分别给出B,C的首项,公差,项数),求有多少种满足条件的等差数列A ?(如果无限种则输出-1)
看样例比较容易理解,对于图中B,C,有十种A满足条件在这里插入图片描述

思路

下用 c i c_i ci 表示 c c c 的第 i i i 项。

  1. 首先考虑0的情况:当 c i c_i ci 中某项未在 b b b 中出现时,答案为0,具体而言, c c c 的公差为 b b b 的公差的倍数且 c 1 , c n c_1,c_n c1,cn 均在 b b b 中出现,是答案非0的充要条件。
  2. 然后考虑-1的情况:观察上图样例, B = {-9,-6,-3,0,3,6,9,12,15,18,21},C = {0,6,12},因为 B 中-6和18的存在限制了A的数量,由此可得:设 c c c 的公差为 d c d_c dc,则答案有限的充要条件是 c 1 − d 和 c n + d c_1 - d和c_n+d c1dcn+d 均在 b b b 中出现。

再考虑如何计算方案数,容易想到如果A的公差可以是 d a d_a da, 那么最多向左、右各延伸 d c / d a − 1 d_c/d_a-1 dc/da1 个,再加上不延伸,方案数为 ( d c / d a ) × ( d c / d a ) (d_c/d_a)\times (d_c/d_a) (dc/da)×(dc/da)
最后考虑如何确定可以取到的 d a d_a da 值,显然必要条件是 d a d_a da d c d_c dc 的因数,这样才能保证C是A的子数列,同时也要保证A与B不会有除C外的其他交集,这就需要 l c m ( d a , d b ) = = d c lcm(d_a, d_b) == d_c lcm(da,db)==dc。具体而言,先用 std::set 存所有 d c d_c dc 的因子,然后对于满足 l c m ( d a , d b ) = = d c lcm(d_a, d_b) == d_c lcm(da,db)==dc d a d_a da,按上述方法累加方案数。

代码

代码前半写的很丑,主要是比赛时对0的情况加了一些并没有用的特判。

void solve() {
    int b, q, y;
    int bc, qc, yc;
    cin >> b >> q >> y >> bc >> qc >> yc;
    if(q > qc) {//都是并没有什么用的特判
    	cout << 0 << endl;
    	return;
    }
    if(bc < b) {
    	cout << 0 << endl;
    	return;
    }
    int t = (bc - b) / q;//b的第t项是c的第一项
    t++;
    if(t > y || (bc-b)%q || t < 1) {//看t是否满足条件
    	cout << 0 << endl;
    	return;
    }
    int mo = bc + (yc-1)*qc;//c的末项
    int s = (mo - b) / q;//b的第s项是c的末项
    s++;
    if(s > y || (mo - b)%q || s < 1) {
    	cout << 0 << endl;
    	return;
    }
    if(gcd(q, qc) == 1 && q != 1) {
    	cout << 0 << endl;
    	return;
    }
    set<int> st;
    st.clear();
    for(int i = 1; i * i <= qc; i++) {
    	if(q == 1) break;
    	if(qc % i == 0) {
    		st.insert(i);
    		st.insert(qc/i);
    	}
    }
    int l = bc - qc;	//c的上一项
    int r = mo + qc;	//c的下一项
    // o(l),o(r);
    if(l < b) {
    	cout << -1 << endl;
    	return;
    }
    int bmo = b + (y-1)*q;//b的末项
    if(r > bmo) {
    	cout << -1 << endl;
    	return;
    }
    int ans = 0;
    for(auto i : st) {
    	if(lcm(i, q) != qc) continue;
    	int cnt = qc/i*qc/i;
    	ans = (ans + cnt) % P;
    }
    if(q == 1) {
    	cout << 1 << endl;
    }
    else
    	cout << ans << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值