【水题】【简单数论】GYM104008(2022CCPC桂林站)E_Draw a Triangle
题目链接:https://codeforces.com/gym/104008/problem/E
题意:给定 $\mathbb{Z}^2$ 上的两个点 $A(x_1,y_1),B(x_2,y_2)$ ,求 $\mathbb{Z}^2$ 上的另一点 $C(x_3,y_3)$ 使得 $S_{\triangle ABC}$ 最小。
思路:显然是要求一个距离直线 $AB$ 最近的整点 $C$,不妨对 $A$ 和 $B$ 进行变换,使 $A$ 为 $(0,0)$,$B$ 为 $(l,h)$,且 $gcd(l,h)=1$。此时直线 $AB$ 的一般式为:$hx-ly=0$。根据距离公式 $d=\frac{|Ax+By+C|}{\sqrt{A^2+B^2}}$,假设 $C(tx,ty)$,则 $d=\frac{|h*tx-l*ty|}{\sqrt{l^2+h^2}}$。因为分母是定值,故我们只需要让 $|h*tx-l*ty|$ 最小即可。因为 $C$ 不能在 $AB$ 上,故该值不能为零;下面尝试让 $|h*tx-l*ty|=1$。
通过画图可知:如果存在一个 $C$ 使得 $h*tx-l*ty=1$,那么一定存在另一个 $C'$ 使得 $h*tx-l*ty=-1$。两边对 $h$ 取模,有 $l*ty\equiv 1 (mod h)$,直接使用 $exgcd$ 求解即可。(注意对竖直线、水平线进行特判)
代码:
#include <bits/stdc++.h>
//#include <windows.h>
#define index xedni
#define y1 fghjwdf
#define x1 ugyjhgu
#define bug cout<<"bug "<<__LINE__<<endl
#define int long long
const int MOD=1e9+7;
//const int MAXN=;
using namespace std;
int gcd(int a,int b) { return !b?a:gcd(b,a%b); }
int exgcd(int a,int b,int &x,int &y)
{
if(a==0 && b==0) return -1;
if(b==0)
{
x=1; y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
int mod_reverse(int a,int n)
{
int x,y; int d=exgcd(a,n,x,y);
if(d==1) return (x%n+n)%n;
else return -1;
}
int h,l,x1,x2,y1,y2;
bool upside;
signed main()
{
cin.tie(0); cout.tie(0);
ios_base::sync_with_stdio(false);
int T; cin>>T;
while(T--)
{
upside=0;
cin>>x1>>y1>>x2>>y2;
if(x1==x2)
{
cout<<x1+1<<" 0"<<endl;
continue;
}
if(y1==y2)
{
cout<<"0 "<<y1+1<<endl;
continue;
}
if(x1>x2)
swap(x1,x2),swap(y1,y2);
if(y1>y2)
swap(y1,y2),upside=1;
h=y2-y1,l=x2-x1;
int G=gcd(h,l); h/=G,l/=G;
int ty,tx;
if(h==1) ty=0,tx=1;
else if(l==1) tx=0,ty=1;
else
{
ty=mod_reverse(l,h);
tx=(ty*l-1)/h;
}
ty+=y1,tx+=x1;
if(upside) ty=y1+y2-ty;
cout<<tx<<" "<<ty<<endl;
}
return 0;
}

浙公网安备 33010602011771号