[ABC290D] Marking
题意简述
接受三个参数 \(N,D,K\)。其中 \(N\) 表示有 \(N\) 个点,标号为 \(0,1,\dots,(N-1)\)。
第一步,将点 \(0\) 标记,并且记录一个数 \(A\),此时 \(A=0\)。
此后,你要进行如下操作:
- \((A+D)\bmod N \to A\)。
- 如果点 \(A\) 已经被标记,\((A+1)\bmod N\to A\)。、
- 标记点 \(A\)。
显然最多进行 \(N\) 次操作。求问第 \(K\) 次操作时标记的点。有多测,\(T\) 组。
题目解析
我们这里称 \(A\) 经过若干次操作后不取模保持小于 \(N\) (也就是说,\(A+D<N\))的过程叫做一轮,每一轮开始时 \(A\) 所处的点叫起点 \(S\)。可以看出,\(0\leq S <D\)。
我们发现这种标记方法的间距始终是相同的,为 \(D\),除非遇到相同的。所以说,一旦起点确定(起点之前未被标记),这一轮之后不会遇到别的被标记的点。可以这么理解:每一轮标记的点都是 \(S+mD,m\in\mathbb{N}\)。设任一标记过的起点为 \(S'\),则其标记过的点为 \(S'+nD,n\in\mathbb{N}\)。假设存在重合的点,则 \(S+mD=S'+nD\),即 \(S'-S=(m-n)D\)。因为 \(S'\neq S\) 且 \(0\leq S,S'<D\),假设不成立。于是不存在重合的点。
那么仅存在某一轮之后与任意起点重合。并且同理可以知道,从本次重合到下一次,经过的轮数相同。具体地说,\(S'-S-l=(m-n)D\)(\(l\) 表示重合的次数,显然 \(l<D\)) 不成立。另一个性质是,每次重合的点下一个必定是未标记的。若是已标记的,则标记已覆盖完 \(N\) 个点了。
所以我们就从最开始的一次重合举例。经过多少步能重合呢?其实就是解同余方程 \(Dx\equiv 0(\bmod N)\)。想到 \(\gcd\)(这种特殊情况就不必劳烦 \(\rm {exgcd}\) 了)。\(Dx\) 什么时候是 \(N\) 的倍数呢?只需要提出 \(N\) 在 \(D\) 中不存在的因数,即 \(N/\gcd(N,D)\)。即: \(x=N/\gcd(N,D)\)。
不重合的时候,就是前进 \(D\) 了,这里不再赘述,可见代码。
一个需要注意的点是:第一次还有一个把 \(0\) 标记了的操作,所以要把 \(K\) 减一。
单次询问复杂度 \(O(\log N)\),整个程序复杂度 \(O(T\log N)\)。
代码实现
#include<cstring>
#include<algorithm>
#include<iostream>
#define R myio::read_int()
//也就是说之后的 R 代表的是读入的返回值
//这里省略了快读
using namespace std;
int T,N,K,D;
signed main(){
T=myio::read_int();
while(T--) {
N=R,D=R,K=R-1;
int ans1=K*D%N; //这一部分表示正常前进的
int ans2=K/(N/__gcd(N,D));
//每 N/__gcd(N,D) 步重合一次,那么就重合了 K/(N/__gcd(N,D)) 次。
myio::print_int(ans1+ans2);
}
return 0;
}

浙公网安备 33010602011771号