【水题】【简单数论】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;
}

 

posted @ 2022-11-06 22:48  I_Am_Danny_CN  阅读(52)  评论(0)    收藏  举报