正题:
首先我们可以知道:它可以覆盖 [ a , b ] , [ 2 a , 2 b ] , [ 3 a , 3 b ] … … [ k a , k b ] ( k > 0 ) [a,b], [2a,2b], [3a,3b]……[ka,kb](k>0) [a,b],[2a,2b],[3a,3b]……[ka,kb](k>0)
那我们来想怎样计算答案
分类讨论
- 未出现区间交集
通过等差数列求和公式,我们可以算出
( ( b − a ) + ( ( p − 1 ) ∗ b − ( p − 1 ) ∗ a ) ) ∗ ( p − 1 ) / 2 ((b-a)+((p-1)*b-(p-1)*a))*(p-1)/2 ((b−a)+((p−1)∗b−(p−1)∗a))∗(p−1)/2
其中p表示当完整的区间的个数
这个式子就是求 [ a , b ] , [ 2 a , 2 b ] , [ 3 a , 3 b ] … … [ p a , p b ] ( p > 0 ) [a,b], [2a,2b], [3a,3b]……[pa,pb](p>0) [a,b],[2a,2b],[3a,3b]……[pa,pb](p>0) 可以覆盖总共的数字个数
化简得
( ( b − a ) + ( ( p − 1 ) ∗ ( b − a ) ) ) ∗ ( p − 1 ) / 2 ((b-a)+((p-1)*(b-a)))*(p-1)/2 ((b−a)+((p−1)∗(b−a)))∗(p−1)/2
p ∗ ( b − a ) ∗ ( p − 1 ) / 2 p*(b-a)*(p-1)/2 p∗(b−a)∗(p−1)/2
然后加上要求的数的位置和区间右端点 − - − 最后一个区间的起始位置即可
即 m i n ( x x , p ∗ b ) − p ∗ a + 1 min(xx,p*b)-p*a+1 min(xx,p∗b)−p∗a+1
为什么是 min \min min 呢,因为直接用xx的话最后有一些没有覆盖到的区间会被算进去,所以要和最后的那个区间的右端点取个 min \min min
所以就是
p ∗ ( p − 1 ) ∗ ( b − a ) / 2 + p − 1 + m i n ( x x , p ∗ b ) − p ∗ a + 1 p*(p-1)*(b-a)/2+p-1+min(xx,p*b)-p*a+1 p∗(p−1)∗(b−a)/2+p−1+min(xx,p∗b)−p∗a+1 - 出现区间交集
其实和上面差不多
k ∗ ( k − 1 ) ∗ ( b − a ) / 2 k*(k-1)*(b-a)/2 k∗(k−1)∗(b−a)/2
其中k代表在区间开始出现交集的那个区间的编号
此式子和上面的化简过程一样
再加上要求的数的位置 − - − 最后一个不完整的区间的起始位置就可以了
即 x x − k ∗ a + 1 xx-k*a+1 xx−k∗a+1
所以就是
k ∗ ( k − 1 ) ∗ ( k − a ) / 2 + k − 1 + x x − k ∗ a + 1 k*(k-1)*(k-a)/2+k-1+xx-k*a+1 k∗(k−1)∗(k−a)/2+k−1+xx−k∗a+1
最后整个代码:
#include<iostream>
#include<cstdio>
using namespace std;
int t;
long long solve(long long n, long long x, long long y)
{
long long a=(x-1)/(y-x)+1;
long long b=n/x;
if(a<=b)
return a*(a-1)*(y-x)/2+a-1+n-a*x+1;//
return b*(b-1)*(y-x)/2+b-1+min(n, b*y)-b*x+1;
}
int main()
{
scanf("%d", &t);
while(t--)
{
long long a, b, x, y;
scanf("%lld%lld%lld%lld", &a, &b, &x, &y);
printf("%lld\n", solve(y, a, b)-solve(x-1, a, b));
}
return 0;
}