题目
题解
设横着每行放
x 个,竖着每排放
y 个,那么显然有一个柿子:
x+1n−xLx+1n−xL−y+1m−yL(x+1)(y+1)ny−xyL+n−xL−xm+xyL−m+yLny+n−xL−xm−m+yLy(n+L)−x(L+M)=y+1m−yL=0=0=0=m−n
设
a=−m−L,b=L+m,那么有:
ax+by=m−n
然后可以用扩展欧几里得算出一组特解
x1,y1,设
g=gcd(a,b),那么通解为
x=x1−kgb,y=y1+kga。
根据题目要求,有:
{1≤x≤⌊Ln⌋1≤y≤⌊Lm⌋
那么我们可以将
x,y 带进去,大力算出
k 的取值范围。
具体来说我们需要分类讨论:
g>0 时
(避免理解出问题,这里还是要提一下,a显然小于0)
x1−kgbk≥1≤⌊gbx1−1⌋
x1−kgb−kgb−kk≤⌊Ln⌋≤⌊Ln⌋−x1≤⌊gb⌊Ln⌋−x1⌋≥⌈gbx1−⌊Ln⌋⌉
y1+kgak≥1≤⌈ga1−y1⌉
y1+kgakgak≤⌊Lm⌋≤⌊Lm⌋−y1≥⌊ga⌊Lm⌋−y1⌋
这样就可以算出
k 的取值范围,假如左端点大于右端点,那么无解输出
0 即可。
g<0 时
将上面所有柿子的结果的符号取反即可。(即
≤ 变成
≥,
≥ 变成
≤)。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define ll long long
ll gcd(ll x,ll y){return y==0?x:gcd(y,x%y);}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)return x=1,y=0,a;
ll re=exgcd(b,a%b,y,x);
return y-=a/b*x,re;
}
ll n,m,L;
ll x,y,a,b,g;
ll min_k,max_k;
int main()
{
scanf("%lld %lld %lld",&n,&m,&L);
a=-m-L;b=n+L;
g=exgcd(a,b,x,y);
if((m-n)%g)return printf("0"),0;
x*=(m-n)/g;y*=(m-n)/g;
if(g>0)
{
min_k=max( (ll)ceil((double)(m/L-y)/(a/g)) , (ll)ceil((double)(x-n/L)/(b/g)) );
max_k=min( (ll)floor((double)(x-1)/(b/g)) , (ll)floor((double)(1-y)/(a/g)) );
}
else
{
min_k=max( (ll)ceil((double)(x-1)/(b/g)) , (ll)ceil((double)(1-y)/(a/g)) );
max_k=min( (ll)floor((double)(x-n/L)/(b/g)) , (ll)floor((double)(m/L-y)/(a/g)) );
}
printf("%lld",max(max_k-min_k+1,0ll));
}