题解 | #智乃的C语言模除方程(another version)#
智乃的C语言模除方程(another version)
https://ac.nowcoder.com/acm/contest/23478/K
K、智乃的C语言模除方程(another version)
首先可以按照上一道题的做法去做(直接改上一题的calcEx即可),但是实际上这道题在思维上更简单了,不需要转化成二维前缀和。
首先你要知道一个叫做整除分块的数论板子。
整除分块是处理形如,其中为循环变量,为什么叫他分块呢,是因为对于形如的式子,对于只有种结果。
然后模除可以改写成一次除法+一次减法的形式,即
这个也是一个经典套路,但凡你碰到这个东西要循环枚举的时候你就可以把它变成,然后尝试整除分块。
对于本题,首先把它变形为,然后在整除分块的过程中就是常数,令其等于。
原式等于,把左侧的看成是一个等差数列。
问题转化成求一个等差数列的第项到第项中有多少项的值在到范围内,显然满足条件的是项到第项中连续的某一段,直接计算即可。
时间复杂度,空间复杂度。
#include<bits/stdc++.h>
using namespace std;
long long _l, _r, _L, _R, P, ans;
long long segment_intersect(long long S1, long long T1, long long S2, long long T2)
{
long long S = max(S1, S2);
long long T = min(T1, T2);
return max(0LL, T - S + 1);
}
long long cal(long long a, long long d, long long base, long long len)
{
if (d == 0)
{
return _l <= a && a <= _r ? segment_intersect(_L, _R, base, base + len) + segment_intersect(_L, _R, -base - len, -base ) : 0;
}
long long s = a > _r ? (a - _r + d - 1) / d : 0;
long long t = a >= _l ? (a - _l) / d : -1;
t = min(t, len - 1);
return segment_intersect(_L, _R, base + s, base + t) + segment_intersect(_L, _R, -base - t, -base - s);
}
int main()
{
scanf("%lld %lld %lld %lld %lld", &P, &_l, &_r, &_L, &_R);
if (P < 0)
{
P = -P;
_l = -_l;
_r = -_r;
swap(_l, _r);
}
_l = max(_l, 0LL);
for (long long l = 1, r; l <= P; l = r + 1)
{
r = P / (P / l);
long long a = P % l;
long long d = P / l;
ans += cal(a, d, l, r - l + 1);
}
ans += cal(P, 0, P + 1, 1000000000);
printf("%lld\n", ans);
return 0;
}