POI Knights

题目链接

题目大意

n(n100) n ( n ≤ 100 ) 个二元组描述了一个骑士的行进方式,遵从如下规则:

若二元组为 (a,b) ( a , b ) ,则骑士从 (x,y) ( x , y ) 出发可以到达 (x+a,y+b) ( x + a , y + b ) (xa,yb) ( x − a , y − b )

两个二元组集合是等价的当且仅当有一个从 (0,0) ( 0 , 0 ) 出发的骑士,经过任意次行进,通过这两组二元组所能到达的格子完全相同。
可以看出对于任何一个二元组集合,都有一个大小为2的集合与它等价,求这个大小为2的集合。

思路

首先可以证明这个集合肯定存在:

将这些行走方式看做一个个向量,则一定存在一组基底可以通过线性组合表示这些向量,因为是二维空间,故基底的个数为2

然后考虑如何求这一组基底。
找一组简单的数据画图可以发现,对于一个固定的横坐标,这一列上所有的点的纵坐标的间距是相等的,同时更换横坐标时这个间距不变。又可以看出,只有某些横坐标才会有点,并且这些横坐标的间距也是相等的。
先考虑求纵坐标上的间距,设间距为 y y 有如下推导:
设两向量分别为(a1,b1),(a2,b2),两向量系数分别为 t1,t2 t 1 , t 2
先考虑最简单的情景,该点在 y y 轴上,我们求(0,0)点正上方的那个点。则可以列出如下式子。

t1a1+t2a2=0t1b1+t2b2=y { t 1 a 1 + t 2 a 2 = 0 t 1 b 1 + t 2 b 2 = y

将第一个式子移项得
t1a1a2=t2 t 1 a 1 a 2 = − t 2

t2 t 2 显然是整数(骑士是不是肯定不能走半步),故
a2  t1a1 a 2   |   t 1 a 1

这个式子的形式并不能说明什么,我们两边同除 gcd(a1,a2) gcd ( a 1 , a 2 ) ,得
a2gcd(a1,a2)  t1a1gcd(a1,a2) a 2 gcd ( a 1 , a 2 )   |   t 1 a 1 gcd ( a 1 , a 2 )

a1=a1gcd(a1,a2),a2=a2gcd(a1,a2) a 1 ′ = a 1 gcd ( a 1 , a 2 ) , a 2 ′ = a 2 gcd ( a 1 , a 2 ) ,可得 gcd(a1,a2)=1 gcd ( a 1 ′ , a 2 ′ ) = 1 ,故
a2  t1 a 2 ′   |   t 1

t1=ka2gcd(a1,a2) t 1 = k a 2 gcd ( a 1 , a 2 )
同理 t2=ka1gcd(a1,a2) t 2 = k a 1 gcd ( a 1 , a 2 ) ,此处 t1,t2 t 1 , t 2 异号。
带回原式得
ka1b2a2b1gcd(a,b)=y k ∗ a 1 b 2 − a 2 b 1 gcd ( a , b ) = y

正上方的点, k=1 k = 1 ,所以有 y=a1b2a2b1gcd(a1,a2) y = a 1 b 2 − a 2 b 1 gcd ( a 1 , a 2 )
求出了这个间距,不禁让我们有了一些想法。

能不能将任意的两个向量(非共线),找到另两个等价向量,满足其中一个与 y y 轴平行?

答案是可以的。怎么做呢?
其中一个向量肯定是(0,a1b2a2b1gcd(a1,a2)),另一个是用来控制横坐标改变和随之而来的纵坐标改变的。
这个向量是 (gcd(a1,a2),y) ( gcd ( a 1 , a 2 ) , y ) ,现在我们来求这个 y y
这个向量的横坐标可以看成是由最初的两个向量按比例缩放而成的,故纵坐标也应该是它按比例缩放的。
让我们想起了一个熟悉的式子。

a1x+a2y=gcd(a1,a2)

熟悉不?其中 x,y x , y 就是缩放的比例。扩展欧几里得即可求解。
则第二个坐标为 b1x+b2y b 1 x + b 2 y

由此我们得到了一个算法:每次找到两个向量,把其中一个变成与 y y 轴平行,另一个继续扔回向量堆中。继续重复算法,直到只剩一个不与y轴平行。此时有一大堆和 y y 轴平行的向量,我们对其纵坐标取一个gcd,即是最终的答案之一,另一个就是剩下的那个没有做此算法的向量。

代码
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<map>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<cctype>
#define pa pair<int,int>
#define INF 0x3f3f3f3f
#define inf 0x3f
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define ull unsigned long long
#define pb push_back

using namespace std;

inline ll read()
{
    long long f=1,sum=0;
    char c=getchar();
    while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
    while (isdigit(c)) {sum=sum*10+c-'0';c=getchar();}
    return sum*f;
}
const int MAXN=110;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if (!b)
    {
        x=1,y=0;
        return a;
    }
    ll tmp,ret;
    ret=exgcd(b,a%b,x,y);
    tmp=x;
    x=y;
    y=tmp-a/b*y;
    return ret;
}
int _gcd(int a,int b)
{
    if (!b) return a;
    return _gcd(b,a%b);
}
int a[MAXN],b[MAXN];
int main()
{
    int n;
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d%d",&a[i],&b[i]);
    for (int i=2;i<=n;i++)
    {
        ll x,y;
        ll gcd=exgcd(a[i-1],a[i],x,y);
        ll tmpa1=0,tmpb1=(a[i-1]*b[i]-a[i]*b[i-1])/gcd;
        ll tmpa2=gcd,tmpb2=b[i-1]*x+b[i]*y;
        a[i-1]=tmpa1,b[i-1]=tmpb1;
        a[i]=tmpa2,b[i]=tmpb2; 
    }
    int nb=b[1];
    for (int i=2;i<n;i++)
        nb=_gcd(nb,b[i]);
    cout<<0<<' '<<nb<<endl<<a[n]<<" "<<b[n];
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值