传送门

CF的题质量真心不低,这道题的标准解法(应该)是exgcd,打比赛的时候想到了具体的推导公式了,也意识到了需要用exgcd,但是因为寝室要锁门了(其实就是太弱,就放弃了。

首先很显然,这条线所经过的总时间应该是$lcm(N,M)$,其实这一点用处不大,但是如果想到了这一点,那么下一步就很好想出来,也就是这整个矩阵的射线轨迹是可以展开在一个$lcm(N,M) \times lcm(N,M)$的矩阵上。到了这一步,只需要把矩形上的每个点铺开在矩阵上然后验证最近的一个点使得在展开的矩阵上横纵坐标相等。

现在问题就转换成了不断的对称一个矩形形成一个正方形,问矩形的每个点在正方形上的坐标。

在进一步就很好看出,对于原矩形上的点$(x,y)$,其在展开的矩形上的对称点应该是$( (k \times 2 \times N) \pm x, (q\times 2\times N) \pm y)$,

然后需要求的就是最小的$ (k \times 2 \times N)  \pm x$使得$(k \times 2 \times N)\pm x = (q\times 2\times N) \pm y$

这个就可以用exgcd解决。

也就是用exgcd求$ax+by=c$的问题,简单说一下就是$a=N \times 2$,而且$b=-M \times 2$,$c=\pm x \pm y$,求解这四个方程的最小正整数解。

//CF 724C
//by Cydiater
//2016.10.9
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <ctime>
#include <cmath>
#include <string>
#include <cstdio>
#include <queue>
#include <map>
using namespace std;
#define ll long long
#define up(i,j,n)        for(ll i=j;i<=n;i++)
#define down(i,j,n)        for(ll i=j;i>=n;i--)
inline ll read(){
    char ch=getchar();ll x=0,f=1;
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll N,M,K,X,Y,ans,oo;
namespace solution{
    ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
    void exgcd(ll a,ll b,ll &x,ll &y){
        if(b==0){x=1;y=0;return;}
        exgcd(b,a%b,x,y);
        ll t=x;x=y;y=t-a/b*y;
    }
    void equ(ll a,ll b,ll c,ll &x,ll &y){
        exgcd(a,b,x,y);ll d=gcd(a,b);
        if(c%d!=0){x=oo;return;}
        ll mod=abs(b/d);
        x*=c/d;
        x=((x%mod+mod)%mod+mod)%mod;
    }
    ll get(ll tx,ll ty){
        ll a=(N<<1),b=-(M<<1),c=ty-tx,x,y;
        equ(a,b,c,x,y);
        if(x==oo)return oo;
        ll ans=2*x*N+tx;
        if(ans<0||ans>=oo)return oo;
        return ans;
    }
    void slove(){
        N=read();M=read();K=read();oo=N*M/gcd(N,M)+1;
        up(i,1,K){
            X=read();Y=read();
            ans=oo;
            ans=min(ans,get(X,Y));
            ans=min(ans,get(-X,Y));
            ans=min(ans,get(-X,-Y));
            ans=min(ans,get(X,-Y));
            if(ans==oo)ans=-1;
            printf("%I64d\n",ans);
        }
    }
}
int main(){
    //freopen("input.in","r",stdin);
    using namespace solution;
    slove();
    return 0;
}
04-19 17:47
查看更多