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

Description

给出两个二维向量a和b,求两个整数 λ1,λ2 不同为0,使得 λ1a+λ2b 最小。

Solution

富榄教我学数学=w=
为了方便我们约定 |a|<|b|,ab>0
如果不是的话可以通过调整 λ 的正负性和交换a,b来完成。
我们要让|ax+by|最小
那么同时也就是让|ax-by|最小
这样就可以用余弦定理了。。。
|axby|=|ax|2+|by|22|ax||by|cosθ
然后你需要知道几个结论:
1:当两个向量的夹角 θ>π3 ,即 cosθ<12
原式

>=|ax|2+|by|2|ax||by|

=(|ax||by|)2+|ax||by|

如果当x,y同时大于0时原式 >=|a|
那么显然当y=0,x=1时原式最小。
2:向量a,b的答案和向量a,b+ka的答案是一致的。
因为|ax+by|=|a(x-ky)+(b+ka)y|
把右边看成向量a和b+ka的 λ 分别是(x-ky,y)
即向量(a,b)和向量(a,b+ka)的答案是一致的。

知道了这些,那么我们就有一个很直观的想法
每次通过选择合适的k,使得向量a,b之间的夹角增大。
那么我们可以通过以下方法来变换向量:
这里写图片描述
显然从b的结尾向a做垂直的角最大。
不行的话我们就想它离垂足越近越好。
也就是那个刚好包含垂足的向量的两端选一个角度最大的。
这样的复杂度看上去是log的,实际上。。。它应该就是log的=w=

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef double db;
int ax,ay,bx,by;
ll solve(ll ax,ll ay,ll bx,ll by) {
    ll d=ax*bx+ay*by;
    if (!ax&&!ay) return 0;
    if (d<0) return solve(ax,ay,-bx,-by);
    db a=sqrt(ax*ax+ay*ay),b=sqrt(bx*bx+by*by);
    ll lena=ax*ax+ay*ay,lenb=bx*bx+by*by;
    db co=d*1.0/a/b;
    if (co<0.5) return min(lena,lenb);
    if (a>b) return solve(bx,by,ax,ay);
    db len=d*1.0/a;
    ll k=len/a;
    if (len-k*a>(k+1)*a-len) k++;
    return solve(ax,ay,bx-k*ax,by-k*ay);
}
int main() {
    freopen("math.in","r",stdin);
    freopen("math.out","w",stdout);
    while (scanf("%d%d%d%d",&ax,&ay,&bx,&by)!=EOF) 
        printf("%lld\n",solve(ax,ay,bx,by));
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值