CF492E Vanya and Field
题目
分析
首先考虑在怎样的情况下才会重复,设原坐标为 \((x+k_1\cdot dx,y+k_1\cdot dy)\) ,设在 \(k_2\) 步重复,也就是:
化简一下:\(\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;
}