K - 青春小M不会梦到兽耳娘 题解 东南大学第六届程序设计竞赛冬季赛(新手组)
K - 青春小M不会梦到兽耳娘 题解
从 \((0,0)\) 出发,有 \(m\) 种移动向量 \((x_i,y_i)\),每种可正反方向无限次使用。判断能否到达 \((0,1),(0,-1),(1,0),(-1,0)\) 四个点。\(T\le10^2\),\(m\le10^2\),\(|x_i|,|y_i|\le100\),每个测试点输出
YES或NO。
本题解核心部分参考官方题解,并使用 DeepSeek 进行部分思路梳理。
我们记录 \(L\) 为 \(m\) 个移动向量能表示的点的集合,记 \(v_i=(x_i,y_i)\) 有
首先考虑只有一个方向怎么做,即每次操作为 \(\pm x_i\),这就是典型的一次不定方程问题,可以由bezouts定理知道,只有当 \(d=\gcd(x_1,x_2,\dots,x_n)=1\) 时,才能到达 \(\pm 1\) 两个位置。
由线性代数的知识,①把一个向量减去另一个向量的整数倍,即 \(v_j\leftarrow v_j-qv_i(q\in \Z)\),\(L\) 不会改变(因为 \(v_j=(v_j-q v_i)+qv_i\) 能线性变换回来);②交换两个向量,\(L\) 也不会改变。
于是我们考虑通过线性变换,将 \(i\geq 2\) 的 \(v_i\) 写成 \((0,y_i')\) 的形式,设经过线性变换后 \(v_1=(x,y)\) ,令 \(g_y=\gcd(y_2',y_3',\dots y_n')\)。可以将 \(L\) 表示成:
当且仅当 \(|x|=1\) 且 \(|g_y|=1\),则有解。
我们来考虑怎么对 \(v_i(i\geq 2)\) 进行变换,考虑用 \(v_1\) 和 \(v_i\) 进行”辗转相除“,目标是使 \(v_{i}=(0,y_i')\)。
首先我们需要保证 \(v_1=(x_1,y_1)\) 中 \(x_1\neq 0\),否则无法消元。
然后求出 \(d=\lfloor \frac{x_1}{x_i}\rfloor\),即为 \(v_1\) 向量需减去 \(v_i\) 的多少倍。类比辗转相除
于是执行
可以发现执行这样的过程,\(v_i\) 最终横坐标会变成 \(0\),且 \(v_1\) 的横坐标为非 \(0\),达成了我们的目的。同时对一个位置只操作了不到 \(O(\log x)\) 次。
具体实现见代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>ttfa;
const int N=500005;
const ll INF=0x3f3f3f3f3f3f3f3f;
int n;
ll x[N],y[N];
int main(){
int Test;scanf("%d",&Test);
while(Test--){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%lld%lld",&x[i],&y[i]);
if(x[i])swap(x[1],x[i]),swap(y[1],y[i]);//保证第一个位置 $x$ 非 0
}
ll gy=0;
for(int i=2;i<=n;++i){
while(x[i]){//使用 (x[1],y[1]) 将这个向量的 x 方向辗转相除为 0
ll len=x[1]/x[i];
ll xx=x[i],yy=y[i];
x[i]=x[1]-len*x[i],y[i]=y[1]-len*y[i];
x[1]=xx,y[1]=yy;
//类比 __gcd(a,b)=__gcd(b,a%b)
}
gy=__gcd(gy,y[i]);
}
if(fabs(gy)==1&&abs(x[1])==1)puts("YES");
else puts("NO");
}
return 0;
}

浙公网安备 33010602011771号