3736. 【NOI2014模拟7.11】数学题(math)

1 篇文章 0 订阅
1 篇文章 0 订阅

(File IO): input:math.in output:math.out

Time Limits: 1000 ms  Memory Limits: 262144 KB  Detailed Limits  

Description

Input

输入有多组测例,每组测例有一行,为4 个整数x1,y1, x2, y2,含义见题目描述。输入文件以EOF 结束。

Output

Sample Input

3 0 1 2

6 0 4 0

Sample Output

5

0

Data Constraint

Source / Author: 黄施霖 math

 

 

 

题目大意 : 

给你两个向量 a,b , 求正整数对x,y,使(ax + by)^1最小 , 输出这个最小值 。 注意x,y不能同时为0。

题解:

二维欧几里得算法 。 (自行bd)

首先 , 对于共线的两个向量 , 答案是 0 。因为你可以让他们先反向 , 然后长度变成他们的lcm ,相加抵消。

 

剩余的情况还要分情况讨论。

为了方便讨论 , 我们始终让 |a| <=|b|且a 点乘b 大于 0 ,也就是两点在y轴的同侧。

 

结论1:当cos\Theta <= \tfrac{1}{2} , 也就是θ >=60度 ,  答案是 |a| ^ 2。

 

注:两向量夹角θ 的 cos , 即cos θ 公式 = a·b / (|a| * |b|).

 

至于为什么θ大于60度cos就小于了0.5 , 自己画图,看看一个大于60的角  ,和一个小于60的角的cos就知道了。

其他证明参考论文。

 

好吧 , 我TM还是有点良心的。

证明:

我们令a为a的模长  , b同理 (本人懒)

设A = 答案模长的平方 = |ax  + by|^2 = (ax)^2 + (by)^2 + 2cosθaxby 

设C = (ax)^2 + (by)^2 - |ax||by| =  (|ax| - |by|)^2 +|ax||by|

我们假设 A >=C 

当x=0 , y不为0 , 则|ax  + by|^2 >= (by)^2 >= (ax)^2  >= a^2

当y=0 , x不为0 , 则|ax  + by|^2 >= (ax)^2 >= a^2 。

当xy都不为0 , |ax||by| >= |a||b| >= a^2

综上 , 当  A >= C ,  答案a^2一定是可以取到的最小值 。

 

接下来证明在cos < 0.5 的情况下 , A >=C 

 当xy > =0 , A >= C显然成立。

当xy<0  , A =  (ax)^2 + (by)^2 + 2cosθaxby  =   (ax)^2 + (by)^2 - 2cosθ|ax||by|

C = (ax)^2 + (by)^2 - |ax||by| .

因为cos <= 0.5 ,2cos <=1

所以 2cosθ|ax||by| <=|ax||by| , 所以 (ax)^2 + (by)^2 - 2cosθ|ax||by| >=  (ax)^2 + (by)^2 - |ax||by| . 即 A>=C,得证 。 

综上 : 无论xy取值如何 , 当cos <= 0.5 , 答案是 |a| ^ 2。

 

结论2 (a,b)这两个向量的答案等于(a , b + ak)这个向量对的答案。

证明 :

令|xa+yb|为所求答案,则|x(a−kb)+(y+kx)b|也是(自己化一下式子),即(a, b)对应着答案(a - kb) , b) , 形式和假设相同 ,得证。

 

这样我们就可以通过不断变换a,b , 直到他们的夹角要大于60度。

 

怎么变换呢 ? 

根据二维欧几里得 , 我们只需求出b在a的投影 : 具体参考“向量的投影”。

如图 : (注意ab的位置和上一幅图不一样) b在a的投影是c。

求投影公式 c(标量) = a * b / |a|;

我们可以计算c是a的k倍 。 有一种简单简洁的计算方式 k = (a*b)/ (a*a) , 后面的a*a其实就是a的模长的平方 ,

即 (a*b)/ (a*a) =[ a * b / |a|] /|a|,这就是k了。

 如果k连1到不到 , 如上图 ,那么 :我们把b变成 b - a

易得角OAB比θ大(做对称)。

若k比1要大了 , 我们就把k下取整 , 这样a*k刚好没超过c 。 然后 b = b - a*k , 也可以起到增大角度的作用。

注意时刻保持我们的约定 : 

|a| <=|b|且a 点乘 b 大于 0 ,也就是两点在y轴的同侧。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define mcy(a,b) memcpy(a,b,sizeof(a))
#define ll long long
#define inf 2000000000
#define N 110
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;


int sqr(int x){return x*x;}

struct vec
{
	int x,y;
	double length(){return sqrt((double)sqr(x) + sqr(y));}
	int operator * (vec b)  {return x * b.x + y*b.y;}
	vec operator * (int b)  {return (vec){x * b , y * b} ;}
	vec operator + (vec b)  {return (vec){x + b.x , y + b.y};}
	vec operator - (vec b)  {return (vec){x - b.x , y - b.y};}
	int operator | (vec b) {return x * b.y + y * b.x;}
}a , b , c;

double get_cos(vec a,vec b)
{
	return (double)(a*b) / (a . length() * b.length()) ; 
}

int main()
{
	open("math");
	while(~scanf("%d%d%d%d",&a. x , &a.y , &b.x ,&b.y) )
	{
		if((a|b) == 0 ) {printf("0");continue;} 
		if(a*a > b*b) swap(a,b);
		if(a*b < 0 ) b = b * -1;
		double c_a = get_cos(a,b);
		while(c_a > 0.5)
		{
			if(a*a > b*b) swap(a,b);
			if(a*b < 0 ) b = b * -1;
			if(a*b / (a*a) <= 1) b = b - a;
			else
			{
				c = a * ((a*b) / (a*a)) ;
				b = b - c;
			}
			c_a = get_cos(a,b);
		}
		printf("%d\n" , min(a*a , b*b) );
	}
	return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值