CF492E Vanya and Field

题目

CF492E Vanya and Field

分析

首先考虑在怎样的情况下才会重复,设原坐标为 \((x+k_1\cdot dx,y+k_1\cdot dy)\) ,设在 \(k_2\) 步重复,也就是:

\[\large (x+k_1\cdot dx,y+k_1\cdot dy)=((x+k_2\cdot dx)\%n,(y+k_2\cdot dy)\%n) \]

化简一下:\(\large x+k_1\cdot dx=x+k_2\cdot dx\ \ \ (mod \ \ n)\)

有: \(\large (k_1-k_2)\cdot dx=0\ \ \ (mod \ \ \ n)\)

对于 \(y\) 来说也是同理,然后因为 \(\large \gcd(dx,n)=\gcd(dy,n)=1\) ,所以有:\(\large k_1-k_2=0\ \ (mod \ \ n)\)

也就是说,一个点一定会在经过了 \(n\) 个点后才会第一次再次来到这个重复点。

这就说明我们选择一个点其实就是选择了一组 \(n\) 个点,这 \(n\) 个点内部构成了一个循环。(也就是选择这一组的任何一个点都等价于选了这个组)

同时,上面的条件也说明了每一组点当中的每个点的横纵坐标都互不相同,换而言之就是同一组的点两两不在同一行或者同一列。

那么很容易发现,第 \(0\) 列的每一个点其实都对应了一组不同点,并且第 \(0\) 列的所有点构成的所有组包含了所有的分组。

于是我们考虑用 \((0,k)\) 作为代表元来表示每一个组的贡献和。

接下来要考虑的就是对于给出的这 \(m\) 个点,如何快速找到其对应的代表元 \((0,d)\)

可以看作是从这个点走对应的环,直到走到 \((0,d)\) 这样的形式。

也就是需要快速求出这样的 \(Dy\) ,满足 \(Dy=k\cdot dy+y,k\cdot dx+x=0\)

\(dy,y,dx,x\) 都已知,于是求出 \(k\) 就能求出 \(Dy\)

这里显然是可以直接 \(exgcd\) 求出 \(dx\) 对于 \(n\) 的逆元,其实也就是预处理一下就行,然后就随便求 \(k\) 了。

但是显然我们也可以先直接预处理对于 \(k=[1,n]\) 的所有 \(-x=k\cdot dx\) ,需要的时候直接从 \(-x\) 里面调用这个 \(k\) 即可。(其实就是个逆映射)

最后就是在所有组当中选择最大的那个组即可,输出点就直接输出代表元就行了。

时间复杂度 \(O(n)\)

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define inc(x,y,mod) (((x)+(y))>=(mod)?(x)+(y)-(mod):(x)+(y))
#define dec(x,y,mod) ((x)-(y)<0?(x)-(y)+(mod):(x)-(y))
#define rep(i,x,y) for(int i=(x);i<=(y);i++)
#define dep(i,y,x) for(int i=(y);i>=(x);i--)
const int N=1e6+5,M=2e5+5,MOD=1e9+7;
int n,m,dx,dy,a[N],Ans[N],Maxn,ex,ey;
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>m>>dx>>dy;
	for(register int i=1,x=0,y=0;i<=n;i++) a[x]=y,x=(x+dx)%n,y=(y+dy)%n;
	for(register int i=1,x,y;i<=m;i++){
		cin>>x>>y;
		y=(y-a[x]+n)%n;
		Ans[y]++;
		if(Ans[y]>Maxn) Maxn=Ans[y],ex=0,ey=y;
	}
	cout<<ex<<' '<<ey;
	return 0;
}



posted @ 2021-09-15 11:09  __Anchor  阅读(38)  评论(0编辑  收藏  举报