标签:扩展欧几里得
题目大意: 对于给定的整数a,b,c,d,及整数x1,x2,y1,y2,z1,z2,求有多少个满足x1<=x<=x2,y1<=y<=y2,z1<=z<=z2且x、y、z均为整数的点在平面ax+by+cz+d=0上。
如果我们枚举z,则问题变成了ax+by=c有多少个解的问题.
这就是一个扩展欧几里得的模板题了.但是有两个地方值得注意:首先我们要保证a,b,c这三个参数必须为正.然后是在利用通解公式求解的范围时要注意向上向下取整的问题.
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1; y=0;
return ;
}
else
{
exgcd(b,a%b,y,x);
y=y-x*(a/b);
}
}
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
ll cal(ll xx1,ll xx2,ll yy1,ll yy2,ll a,ll b,ll m)
{
///一定要保证a,b,m为非负
if(m<0)
{
m=-m; a=-a; b=-b;
}
if(a<0)
{
a=-a; ll tmp=xx1;
xx1=-xx2;
xx2=-tmp;
}
if(b<0)
{
b=-b;
ll tmp=yy1;
yy1=-yy2;
yy2=-tmp;
}
ll x0,y0,d1,t1,t2,t3,t4;
ll x1,y1;
d1=gcd(a,b);
if(m%d1!=0) return 0;
a/=d1; b/=d1; m/=d1;
exgcd(a,b,x0,y0);
x0*=m,y0*=m;
///因为是关于t的单调函数,所以对于下边界要向上取整
///对于上边界要向下取整
t1=ceil(double(xx1-x0)/b); t2=floor(double(xx2-x0)/b);
t3=ceil(double(y0-yy2)/a); t4=floor(double(y0-yy1)/a);
t1=max(t1,t3); t2=min(t2,t4);
if(t2<t1) return 0;
return t2-t1+1;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("test.txt","w",stdout);
ll a,b,c,d;
ll x1,x2,y1,y2,z1,z2;
while(scanf("%lld%lld%lld%lld",&a,&b,&c,&d)!=EOF)
{
scanf("%lld%lld%lld%lld%lld%lld",&x1,&x2,&y1,&y2,&z1,&z2);
if(a==0&&b==0)
{
ll cnt=0;
for(int z=z1;z<=z2;z++)
{
if(c*z+d==0)
cnt++;
}
cnt*=(x2-x1+1)*(y2-y1+1);
printf("%lld\n",cnt);
continue;
}
if(a==0)
{
ll cnt=0;
for(int z=z1;z<=z2;z++)
{
ll tmp=-1*(d+c*z);
if(tmp%b==0&&tmp/b>=y1&&tmp/b<=y2)
cnt++;
}
cnt*=(x2-x1+1);
printf("%lld\n",cnt);
continue;
}
if(b==0)
{
ll cnt=0;
for(int z=z1;z<=z2;z++)
{
ll tmp=-1*(c*z+d);
if(tmp%a==0&&tmp/a>=x1&&tmp/a<=x2)
cnt++;
}
cnt*=(y2-y1+1);
printf("%lld\n",cnt);
continue;
}
ll ans=0;
for(int z=z1;z<=z2;z++)
{
ll m=-1*(d+c*z);
ans+=(cal(x1,x2,y1,y2,a,b,m));
}
printf("%lld\n",ans);
}
return 0;
}
标签:扩展欧几里得
原文地址:http://blog.csdn.net/acm_lkl/article/details/45747603