[NOI2014模拟][数学推导/二维欧几里得]数学题

题面

给出两个向量 a ⃗ = ( x 0 , y 0 ) \vec a=(x_0,y_0) a =(x0,y0), b ⃗ = ( x 1 , y 1 ) \vec b=(x_1,y_1) b =(x1,y1),求整数 x x x, y y y满足不同时为 0 0 0,使 ∣ x a ⃗ + y b ⃗ ∣ |x\vec a + y \vec b| xa +yb 最小。
T T T组数据,每组数据输出 ∣ x a ⃗ + y b ⃗ ∣ 2 |x\vec a + y \vec b|^2 xa +yb 2

对于20%的数据点: T ≤ 100 T≤100 T100,存在一组最优的 ( x , y ) (x,y) (x,y),满足 ∣ x ∣ , ∣ y ∣ ≤ 30 |x|,|y|≤30 x,y30
对于60%的数据点: T ≤ 10000 T≤10000 T10000,存在一组最优的 ( x , y ) (x,y) (x,y),满足 min ⁡ { ∣ x ∣ , ∣ y ∣ } ≤ 30 \min \{|x|,|y|\}≤30 min{x,y}30
对于100%的数据点: T ≤ 100000 T≤100000 T100000, ∣ x 0 ∣ , ∣ y 0 ∣ , ∣ x 1 ∣ , ∣ y 1 ∣ ≤ 1 0 4 |x_0|,|y_0|,|x_1|,|y_1|≤10^4 x0,y0,x1,y1104,存在一组最优的 ( x , y ) (x,y) (x,y),满足 ∣ x ∣ , ∣ y ∣ ≤ 1 0 4 |x|,|y|≤10^4 x,y104

解法1

( k 1 , k 2 ) (k_1,k_2) (k1,k2)为两整数。
我们的任务就是求 ( k 1 x 1 + k 2 x 2 ) 2 + ( k 1 y 1 + k 2 y 2 ) 2 (k_1x_1+k_2x_2)^2+(k_1y_1+k_2y_2)^2 (k1x1+k2x2)2+(k1y1+k2y2)2的最小值。
展开得:
( x 1 2 + y 1 2 ) k 1 2 + 2 ( x 1 x 2 + y 1 y 2 ) k 1 k 2 + ( x 2 2 + y 2 2 ) k 2 2 (x_1^2+y_1^2)k_1^2+2(x_1x_2+y_1y_2)k_1k_2+(x_2^2+y_2^2)k_2^2 (x12+y12)k12+2(x1x2+y1y2)k1k2+(x22+y22)k22
我们可以将式子看成一个以 k 1 k_1 k1为自变量的函数。
A = x 1 2 + y 1 2 , B = x 1 x 2 + y 1 y 2 , C = x 2 2 + y 2 2 A=x_1^2+y_1^2,B=x_1x_2+y_1y_2,C=x_2^2+y_2^2 A=x12+y12,B=x1x2+y1y2,C=x22+y22
原式化为 A × k 1 2 + 2 B × k 2 × k 1 + C × k 2 2 A\times k_1^2+2B\times k_2\times k_1+C\times k_2^2 A×k12+2B×k2×k1+C×k22
根据二次函数顶点式得 k 1 = − B A k 2 k_1=-\frac{B}{A}k_2 k1=ABk2时函数取到最小值。
那么我们就能够得到一个60分的算法了,每次枚举 k 1 k_1 k1 k 2 k_2 k2解出与顶点最接近的另一个数即可。
考虑继续化简,由于实际的 k 1 k_1 k1与取到最小的 k 1 k_1 k1最多相差 1 2 \frac{1}{2} 21,我们可以尝试将 k 1 k_1 k1代入先消掉。
得: ( C − B 2 A ) k 2 2 (C-\frac{B^2}{A})k_2^2 (CAB2)k22
代入A,B,C,得:
( x 1 2 + y 1 2 ) ( x 2 2 + y 2 2 ) − ( x 1 x 2 + y 1 y 2 ) 2 x 1 2 + y 1 2 k 2 2 \frac{(x_1^2+y_1^2)(x_2^2+y_2^2)-(x_1x_2+y_1y_2)^2}{x_1^2+y_1^2}k_2^2 x12+y12(x12+y12)(x22+y22)(x1x2+y1y2)2k22
拆项:
x 1 2 y 2 2 + x 2 2 y 1 2 − 2 x 1 x 2 y 1 y 2 x 1 2 + y 1 2 k 2 2 \frac{x_1^2y_2^2+x_2^2y_1^2-2x_1x_2y_1y_2}{x_1^2+y_1^2}k_2^2 x12+y12x12y22+x22y122x1x2y1y2k22
合并:
( x 1 y 2 − x 2 y 1 ) 2 x 1 2 + y 1 2 k 2 2 \frac{(x_1y_2-x_2y_1)^2}{x_1^2+y_1^2}k_2^2 x12+y12(x1y2x2y1)2k22
可以发现这一定是一个非负数,而且仅仅只与 k 2 k_2 k2有关。
这样我们就可以将原式写成一个顶点式了:
A ( k 1 + B A k 2 ) 2 + ( x 1 y 2 − x 2 y 1 ) 2 x 1 2 + y 1 2 k 2 2 A(k_1+\frac{B}{A}k_2)^2+\frac{(x_1y_2-x_2y_1)^2}{x_1^2+y_1^2}k_2^2 A(k1+ABk2)2+x12+y12(x1y2x2y1)2k22
A > 0 A>0 A>0,因此:
1 A ( A k 1 + B k 2 ) 2 + 1 A ( x 1 y 2 − x 2 y 1 ) 2 k 2 2 \frac{1}{A}(Ak_1+Bk_2)^2+\frac{1}{A}(x_1y_2-x_2y_1)^2k_2^2 A1(Ak1+Bk2)2+A1(x1y2x2y1)2k22
至此,我们可以将式子划分为两部分,一部分只与 k 2 k_2 k2,一部分与 k 1 , k 2 k_1,k_2 k1,k2之间的最小距离有关。
由于 k 1 , k 2 k_1,k_2 k1,k2之间的最小距离在 1 2 \frac{1}{2} 21范围内,因此可以计算出第一个部分的范围。
根据前文有:
− B A k 2 − 1 2 ≤ k 1 ≤ − B A k 2 + 1 2 -\frac{B}{A}k_2-\frac{1}{2}≤k_1≤-\frac{B}{A}k_2+\frac{1}{2} ABk221k1ABk2+21
− 1 2 A ≤ k 1 A + B k 2 ≤ 1 2 A -\frac{1}{2}A≤k_1A+Bk_2≤\frac{1}{2}A 21Ak1A+Bk221A
因此: ( k 1 A + B k 2 ) 2 ≤ 1 4 A 2 (k_1A+Bk_2)^2≤\frac{1}{4}A^2 (k1A+Bk2)241A2
我们还发现当 k 2 > 1 , ( x 1 y 2 − x 2 y 1 ) 2 > 0 k_2>1,(x_1y_2-x_2y_1)^2>0 k2>1,(x1y2x2y1)2>0时, ( x 1 y 2 − x 2 y 1 ) 2 k 2 2 (x_1y_2-x_2y_1)^2k_2^2 (x1y2x2y1)2k22的增长速度实际上相当的快。
因此我们可以直接从 1 1 1开始枚举 k 2 k_2 k2,由于后面的部分一定是递增的,前面的部分有一定的范围,因此当后面的部分与 k 2 = 1 k_2=1 k2=1取到的值已经相差 1 4 A 2 \frac{1}{4}A^2 41A2时,我们就可以不再枚举了。
由于 1 4 A 2 ≤ 1 0 8 \frac{1}{4}A^2≤10^8 41A2108
因此这样做的时间复杂度为 O ( T C ) O(T\sqrt C) O(TC )的。
其中 C C C小于 1 0 8 10^8 108

解法2

见金斌的集训队论文《欧几里得算法的应用》-二维欧几里得算法
比第一种方法简单多了,虽然时间复杂度不明…

实现

这是解法2的实现,解法1的懒得写了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define LL long long
void exgcd(LL x1,LL y1,LL x2,LL y2,LL &a,LL &b)
{
	LL x=x1*x2+y1*y2,y=x1*x1+y1*y1,z=x2*x2+y2*y2;
	if(x<0){
		exgcd(-x1,-y1,x2,y2,a,b),a=-a;
		return ;
	}
	if(4*x*x<y*z||x==0){
		if(y>z)b=1;
		else a=1;
		return ;
	}
	bool F=0;
	if(y<z)swap(x1,x2),swap(y1,y2),F=1;
	y=x1*x1+y1*y1,z=x2*x2+y2*y2;
	LL t=x/z,t1=t+1;
	if(x-t*z<=t1*z-x&&t)
		exgcd(x1-t*x2,y1-t*y2,x2,y2,a,b),b-=(t*a);
	else exgcd(x1-t1*x2,y1-t1*y2,x2,y2,a,b),b-=(t1*a);
	if(F)swap(a,b);
}
int main()
{
	freopen("math.in","r",stdin);
	freopen("math.out","w",stdout);
	LL x1,x2,y1,y2;
	while(~scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2))
	{
		LL a=0,b=0;
		exgcd(x1,y1,x2,y2,a,b);
		LL res1=(x1*a+x2*b)*(x1*a+x2*b),res2=(y1*a+y2*b)*(y1*a+y2*b);
		printf("%lld\n",res1+res2);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值