【NOI2014模拟7.11】数学题

Description

这里写图片描述

Solution

这题是一道很奇妙的题目。
首先在共线或者夹角非常小的时候,答案是 gcd(|X|,|Y|) ,那么考虑一下,是否和gcd有关。
明显很难有关系,但是这种思想很重要类欧几里得算法,考虑把(X,Y)这个向量转化成(X’,Y’)这个向量。
首先需要的是边界条件。
我们可以知道在θ大于60°的时候,答案为min(|X|,|Y|)。
设X=(c,0),Y=(e,f),设 |X||Y|
证明: |aX+bY|=(c+e)2+f2=c2+e2+2ce+f2
=|aX|2+|bY|2+2cosθ|aX||bY|
|aX|2+|bY|22cosθ|aX||bY|
2cosθ1
|aX|2+|bY|2|aX||bY|
(|aX||bY|)2+|aX||bY|
所以显然在|X|=0或|Y|=0答案只会≥|X|。
否则无论a,b取什么值(不能同时=0)|aX||bY|≥|X|
所以得证:边界是θ≥60°的时候,ans=|X|(如果|X|>|Y|那么交换),就是前面的系数是(1,0)。
那么现在的关键就是要把角度不断的变大。
首先考虑如何正确的转化(X,Y)。
|aX+bY|=|aXakY+bY+akY|=|a(XkY)+(b+ak)Y|
就是说向量(X,Y)和向量(X-kY,Y)的答案是等价的,可以互相转化,但是系数不同,前者是(a,b),后者是(a,b+ak)。
那么我们现在要考虑k=?的时候,角度可以变大。
这里写图片描述
设OC=kX,BE⊥OD,OD=(k+1)X,OA=X,OB=Y。
当E落在OA上的时候,此时显然有角OAB>角AOB,那么可以把(X,Y)转化成(Y,X)=>(Y-X,X)(k=1)及(OB-OA,OA)及(AB,OA)。此时是可以把角度变大的。
当E落在OA外的时候:
1、当|CE|>|ED|时就取较大的角ODB及转化为
(OA,OB)=>(OB,OA)=>(OB-(k+1)OA,OA)=>(DB,OA)
2、否则取较大的角DBC及转化为
(OA,OB)=>(OB,OA)=>(OB-kOA,OA)=>(CB,OA)
此时注意(a,b)系数转化完之后的变化。
其实不用特殊考虑E落在OA外的时候,第二种情况可以顺便考虑进去及(Y-(k+1)X,X)落在里面是k=0,及(Y-X,X)。
还要考虑的细节
1、角度的判断用向量的点积。
2、当角度>90°时,及点积<0,此时(X,Y)转化为(X,-Y),对应的系数也要变成(a,-b)
3、求k的时候可以直接用点积整除以(X的模长的平方)
证明:
OE=|Y|cosθOE|X|=|Y|cosθ|X|=k
X=(x1,y1),Y=x2,y2
|X||Y|cosθ=x1x2+y1y2()
所以 |Y|cosθ=x1x2+y1y2|Y|
所以 x1x2+y1y2|X|2=k

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
ll i,j,k,l,t,n,m,ans,x,y,xx,yy,a,b;
ll sqr(ll x){return x*x;}
void leiou(ll &a,ll &b,ll x,ll y,ll xx,ll yy){
    ll ji=x*xx+y*yy,amo=x*x+y*y,bmo=xx*xx+yy*yy;
    if(ji<0){
        leiou(a,b,x,y,-xx,-yy);
        b=-b;
        return;
    }
    if(amo>bmo){
        leiou(b,a,xx,yy,x,y);
        return;
    }
    if(ji*ji*4<amo*bmo|!amo){
        a=1,b=0;
        return;
    }
    ll k=ji/amo;
    if(2*ji>(2*k+1)*amo){
        leiou(a,b,x,y,xx-(k+1)*x,yy-(k+1)*y);
        a-=(k+1)*b;
    }
    else{
        leiou(a,b,x,y,xx-k*x,yy-k*y);
        a-=k*b;
    }
}
int main(){
//  freopen("math.in","r",stdin);
//  freopen("math.out","w",stdout);
    freopen("fan.in","r",stdin);
    freopen("fan.out","w",stdout);
    while(scanf("%lld%lld%lld%lld",&x,&y,&xx,&yy)!=EOF){
        leiou(a,b,x,y,xx,yy);
        printf("%lld\n",sqr(a*x+b*xx)+sqr(a*y+b*yy));
    }
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值