题目大意
有 n(n≤100) n ( n ≤ 100 ) 个二元组描述了一个骑士的行进方式,遵从如下规则:
若二元组为 (a,b) ( a , b ) ,则骑士从 (x,y) ( x , y ) 出发可以到达 (x+a,y+b) ( x + a , y + b ) 或 (x−a,y−b) ( x − a , y − b )
两个二元组集合是等价的当且仅当有一个从
(0,0)
(
0
,
0
)
出发的骑士,经过任意次行进,通过这两组二元组所能到达的格子完全相同。
可以看出对于任何一个二元组集合,都有一个大小为2的集合与它等价,求这个大小为2的集合。
思路
首先可以证明这个集合肯定存在:
将这些行走方式看做一个个向量,则一定存在一组基底可以通过线性组合表示这些向量,因为是二维空间,故基底的个数为2
然后考虑如何求这一组基底。
找一组简单的数据画图可以发现,对于一个固定的横坐标,这一列上所有的点的纵坐标的间距是相等的,同时更换横坐标时这个间距不变。又可以看出,只有某些横坐标才会有点,并且这些横坐标的间距也是相等的。
先考虑求纵坐标上的间距,设间距为
y
y
有如下推导:
设两向量分别为,两向量系数分别为
t1,t2
t
1
,
t
2
。
先考虑最简单的情景,该点在
y
y
轴上,我们求点正上方的那个点。则可以列出如下式子。
将第一个式子移项得
t2 t 2 显然是整数(骑士是不是肯定不能走半步),故
这个式子的形式并不能说明什么,我们两边同除 gcd(a1,a2) gcd ( a 1 , a 2 ) ,得
设 a′1=a1gcd(a1,a2),a′2=a2gcd(a1,a2) a 1 ′ = a 1 gcd ( a 1 , a 2 ) , a 2 ′ = a 2 gcd ( a 1 , a 2 ) ,可得 gcd(a′1,a′2)=1 gcd ( a 1 ′ , a 2 ′ ) = 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 异号。
带回原式得
正上方的点, k=1 k = 1 ,所以有 y=a1b2−a2b1gcd(a1,a2) y = a 1 b 2 − a 2 b 1 gcd ( a 1 , a 2 )
求出了这个间距,不禁让我们有了一些想法。
能不能将任意的两个向量(非共线),找到另两个等价向量,满足其中一个与 y y 轴平行?
答案是可以的。怎么做呢?
其中一个向量肯定是,另一个是用来控制横坐标改变和随之而来的纵坐标改变的。
这个向量是
(gcd(a1,a2),y)
(
gcd
(
a
1
,
a
2
)
,
y
)
,现在我们来求这个
y
y
。
这个向量的横坐标可以看成是由最初的两个向量按比例缩放而成的,故纵坐标也应该是它按比例缩放的。
让我们想起了一个熟悉的式子。
熟悉不?其中 x,y x , y 就是缩放的比例。扩展欧几里得即可求解。
则第二个坐标为 b1x+b2y b 1 x + b 2 y 。
由此我们得到了一个算法:每次找到两个向量,把其中一个变成与 y y 轴平行,另一个继续扔回向量堆中。继续重复算法,直到只剩一个不与轴平行。此时有一大堆和 y y 轴平行的向量,我们对其纵坐标取一个,即是最终的答案之一,另一个就是剩下的那个没有做此算法的向量。
代码
#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;
}