把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷3421】[POI2005] SKO-Knights(构造)

点此看题面

  • 给定马的\(n\)种走法\((a_i,b_i)\)(可以选择将横纵坐标同时加/减\(a_i,b_i\))。
  • 要求你给出两种走法,使得仅通过这两种走法能到达的所有位置与原先相同。
  • \(n\le100\)

走法的合并

先考虑如何合并两种走法\((a,b),(c,d)\)

首先要知道一个结论:肯定存在一种构造方案,使得最终的两种走法中存在至少一种是竖直的(也可以说至少一种是水平的,看个人喜好)。

要让能到达的位置相同,首先必须满足能到达的横坐标完全相同,而我们选取了一种走法是竖直的,另一种就必须要满足能够到达所有横坐标。

因此我们通过\(exgcd\)求出\(ap+cq=gcd(a,c)\)的一组解,则这非竖直走法可以构造成\((ap+cq,bp+dq)\),不妨记作\((A,B)\)

而竖直走法就考虑仅通过\((A,B)\)分别走到\(x=a,x=c\)时所需的调整,应该是\((0,gcd(b-\frac aA\times B,d-\frac cA\times B))\)

一次合并得到了一种非竖直走法和一种竖直走法,非竖直走法直接扔回去继续搞(相当于一次操作减少了一种非竖直走法),而多种竖直走法的合并只要给第二维取\(gcd\)即可,非常容易。

代码:\(O(nlogV)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100
using namespace std;
int n,a[N+5],b[N+5];I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
I void exgcd(CI x,CI y,int& a,int& b) {y?(exgcd(y,x%y,b,a),b-=x/y*a):(a=1,b=0);}
int main()
{
	RI i;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",a+i,b+i);
	RI p,q,A,B,t=0;for(i=2;i<=n;++i) exgcd(a[i-1],a[i],p,q),//exgcd求出一组解
		A=a[i-1]*p+a[i]*q,B=b[i-1]*p+b[i]*q,t=gcd(t,gcd(b[i-1]-a[i-1]/A*B,b[i]-a[i]/A*B)),a[i]=A,b[i]=B;//更新非竖直走法,竖直走法第二维取gcd
	return printf("0 %d\n%d %d\n",t,a[n],b[n]),0;
}
posted @ 2021-05-27 15:44  TheLostWeak  阅读(164)  评论(0编辑  收藏  举报