link
思维
题意
设两个等差数列A,B公共序列为C,
现在给你两个等差数列B,C(输入两行分别给出B,C的首项,公差,项数),求有多少种满足条件的等差数列A ?(如果无限种则输出-1)
看样例比较容易理解,对于图中B,C,有十种A满足条件
思路
下用 c i c_i ci 表示 c c c 的第 i i i 项。
- 首先考虑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的充要条件。
- 然后考虑-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 c1−d和cn+d 均在 b b b 中出现。
再考虑如何计算方案数,容易想到如果A的公差可以是
d
a
d_a
da, 那么最多向左、右各延伸
d
c
/
d
a
−
1
d_c/d_a-1
dc/da−1 个,再加上不延伸,方案数为
(
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;
}