Codeforces 710D Two Arithmetic Progressions 题解 [ 绿 ] [ 值域分治 ] [ 剩余系 ] [ exgcd ]

简单根号分治题。
负数显然不好搞,我们先从 \(b,d\) 其中一个作为原点,这样更容易处理。
然后考虑做法,发现这个东西是一个不断跳跃的形式,很像根号分治的板题,于是对步长(公差)考虑根号分治:
- \(\min(a,c) \ge B\)
- 此时最多只会跳 \(10^6\) 左右步,就一定能跳到边界,暴力跳即可。时间复杂度 \(O(\dfrac{V}{B})\)。
- \(\min(a,c) \le B\)
- 因为 \(a,c\) 很小,所以从循环节的角度考虑,先钦定 \(a,c\) 当中小的那一个作为模数,然后就是求一个模 \(\min(a,c)\) 的剩余系。为了方便,我们考虑将 \(a,c\) 中更小的那一个数列作为有原点的那个数列,那么更大的那一个数跳的路线就是先跳若干步进入循环节,然后在循环节中以固定的步数不断跳跃。因为模数很小,所以循环节的长度也很小,直接暴力求循环节和起始点即可。如果跳了 \(\min(a,c)\) 次都找不到就说明无解。时间复杂度 \(O(B)\)。
平衡一下发现 \(B = \sqrt{V}\) 的时候复杂度最优,于是总体时间复杂度就是 \(O(\sqrt{V})\) 的。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
ll a, b, c, d;
ll solve(ll lmt)
{
ll now = d, ans = 0;
if(a >= sqrt(lmt))
{
while(now <= lmt)
{
ans += (now % a == 0);
now += c;
}
return ans;
}
bool flag = 0;
for(int i = 1; i <= a && now <= lmt; i++)
{
if(now % a == 0)
{
ans++;
flag = 1;
break;
}
now += c;
}
if(flag == 0) return ans;
ll st = now;
flag = 0;
for(int i = 1; i <= a && now <= lmt; i++)
{
now += c;
if(now % a == 0)
{
flag = 1;
break;
}
}
if(flag == 0) return ans;
ll gap = now - st;
return (ans + (lmt - st) / gap);
}
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ll l, r;
cin >> a >> b >> c >> d >> l >> r;
if(a > c) swap(b, d), swap(a, c);
d -= b; l -= b; r -= b; b = 0;
if(d < 0) d += ceil(1.0 * (-d) / c) * c;
cout << solve(r) - solve(l - 1);
return 0;
}

浙公网安备 33010602011771号