AtCoder Beginner Contest 290(D)

昨天做到一个题发现太精彩了,学会了之后给大家分享一下数学证明

思路来源题解,只作自己的解释证明

首先我们假设有两个数A,B,设gcd(A,B)=g,则可表示A=ag,B=bg,因为A和B都是g的倍数,所以A%B也一定是g的倍数(A%B=A-(A/B)*B=ag-(a/b)*bg),且0,A%B,2A%B,3A%B....(b-1)A%B 都是g的倍数且互不相同

证明:

0,ag(1-1/b*b),2ag(1-1/b*b),......(b-1)ag(1-1/b*b),其中(1-1/b*b)不为0,而0,1,2,3...(b-1)互不相同,故0,A%B,2A%B,3A%B....(b-1)A%B都是g的倍数且互不相同

知道了这个性质,我们可以在这个题应用到

 

首先,我们求出g=__gcd(D,N),D=dg,,N=ng,

第一次标记了0,按题面要求执行操作N-1次,第2次标记(0+D)%N,第3次标记(2D)%N,第4次标记(3D)%N.....我们可以发现这个式子刚好符合上面推出来的,故0,(2D)%N,(3D)%N,.......((n-1)D)%N

所以这前n个格子是互不相同一定优先被标记的,且在第i次标记的格子下标为(i-1)D%N,1<=i<=n

 

然后,我们来考虑接下来可能会遇到被标记过格子的情况

在第n+1次,我们应该标记下标x=(n+1-1)D%N的格子,但是这一步nD%N==ndg%N==dN%N==0,而0是已经被标记过了的,所以我们要让x=(x+1)%N=1+x%N=1+(1-1)%N

在第n+2次,我们应该标记下标x=(n+2-1)D%N的格子,这一步x==(n+1)D%N==(n+1)dg%N==(ndg)%N+D%N==D%N,但是D%N这个格子也被标记过了,所以标记x==(x+1)%N==(1+(n+1)%N)%N==1+(2-1)%N

以此类推

.

.

.

在第n+i次,我们应该标记下标x=(n+i-1)D%N的格子,这一步标记x==(n+i)D%N==1+(i-1)%N

这又有n次标记,1<=i<=n

 

又在接下来的n次中,在第2n+i次,我们应该标记下标x=(2n+i-1)的风格是2+(i-1)D%N,

这又有n次标记,1<=i<=n

以此类推

.

.

.

个人理解就是在一个数轴上从0开始的不断倍数跳跃填n个点,每n个点往后移动1(因为前面的点已经被填了只能往后移动试探填点),感觉好像欧拉筛那个筛法?(不是很懂求大佬评论区解答)

 

在这个题求第k个数对应的格子,就是看经过了几个最大公约数产生的相对零点偏移加上对应的初始格子,感觉自己这样理解豁然开朗,所以就可以写码了!

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N=1e6+10;
int m,t;
void solve(){
    int n,d,k;
    cin>>n>>d>>k;
    int gcd=n/__gcd(n,d);
    //cout<<gcd<<endl;
    //cout<<(k-1)/gcd<<endl;
    //cout<<(k-1)*d%n<<endl;
    cout<<(k-1)/gcd+(k-1)*d%n<<endl;
    return;
}
signed main(){ 
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int _;
    cin>>_;
    //_=1;
    while(_--)solve();
    return 0;
}

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值