AtCoder Beginner Contest 290 D - Marking (结合官方题解的证明和解析) 思维 + 裴蜀定理

文章介绍了D-Marking问题,涉及到在给定的NxN网格中,按照特定规则粉刷格子的过程。关键在于应用裴蜀定理分析跳跃模式,找到粉刷的周期性。通过计算gcd(N,D)和理解跳跃序列的周期,可以确定第K次粉刷的位置。解题策略包括计算跳跃轮数(len)和偏移量(add),最后得出答案ans。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

D - Marking

题目链接:D - Marking
官方题解链接:Editorial

题意

给定 N , D , K N, D, K N,D,K
经过的每个格子都会被粉刷 从 i d x = 0 idx = 0 idx=0出发(第一次粉刷第 0 0 0格)
1. 每次向前跳 D D D ( i d x + D ) m o d    N (idx + D) \mod N (idx+D)modN,若该格子未被粉刷则粉刷,继续执行第 1 1 1步。
2. 若之前被粉刷过则向前一直走 ( i d x + 1 ) m o d    N (idx + 1) \mod N (idx+1)modN到没粉刷的格子为止,粉刷该格子后回到第 1 1 1步。
问第 K K K次粉刷的是哪个格子。

前置知识 (裴蜀定理有关)

首先有 结论: g = g c d ( A , B ) g = gcd(A, B) g=gcd(A,B) A = a g A = ag A=ag B = b g B = bg B=bg
a a a项式子 0 B m o d    A 0B \mod A 0BmodA B m o d    A B \mod A BmodA 2 B m o d    A 2B \mod A 2BmodA,……, ( a − 1 ) B m o d    A (a - 1)B \mod A (a1)BmodA
1.每项式子的值都是 g g g的倍数 。
2.且倍数为 0 ∼ a − 1 0 \sim a - 1 0a1每一个数恰好出现一次。

证明:

  1. B m o d    A = B − x A ( x = B / A ) = b g − x a g = ( b − x a ) g B \mod A = B - xA (x = B / A) = bg - xag = (b - xa)g BmodA=BxA(x=B/A)=bgxag=(bxa)g
  2. 设有重复的倍数 i B m o d    A = j B m o d    A ( i ! = j ) iB \mod A = jB \mod A (i != j) iBmodA=jBmodA(i!=j)
    i B − j B = ( i − j ) b g = y A iB - jB = (i - j)bg = yA iBjB=(ij)bg=yA A A A 的倍数 ( i B − j B ) m o d    A = 0 (iB - jB) \mod A = 0 (iBjB)modA=0 A A A 余数相同,那么相减后再模 A A A 余数自然为 0 0 0
    ( i − j ) b g = y a g (i - j)bg = yag (ij)bg=yag
    因为 a a a b b b 互质( a a a b b b 若还有大于 1 1 1 的公约数,那么 g c d gcd gcd 就不会是 g g g 而是 g ∗ g c d ( a , b ) g * gcd(a, b) ggcd(a,b))。
    所以 ( i − j ) (i - j) (ij) a a a 的倍数 这显然与上式系数区间 [ 0 , a − 1 ] [0, a - 1] [0,a1]相违背。

解题思路

g = g c d ( N , D ) g = gcd(N, D) g=gcd(N,D) N = n g N = ng N=ng D = d g D = dg D=dg
对于上述问题有跳跃的前 n n n 步为 0 m o d    N 0 \mod N 0modN D m o d    N D \mod N DmodN 2 D m o d    N 2D \mod N 2DmodN,…… , ( n − 1 ) D m o d    N (n - 1)D \mod N (n1)DmodN
每一项都是不重复的 g g g 的倍数,所以不会触发条件 2 2 2。 而第 n + 1 n + 1 n+1 n D m o d    N = n d g = d N m o d    N = 0 nD \mod N = ndg = dN \mod N = 0 nDmodN=ndg=dNmodN=0
开始有重复项第二轮开始,此时需要向前走一步 ( 0 (0 % N) + 1 (0,接下来又可以按 ( D m o d    N ) + 1 (D \mod N) + 1 (DmodN)+1 ( 2 D m o d    N ) + 1 (2D \mod N) + 1 (2DmodN)+1,……的顺序进行 n n n 轮跳跃。
每一轮 n n n 个直到所有格子都被遍历到。所以我们只需要确定我们在哪一轮,偏移量 + 轮数即可
轮数: l e n = ( K − 1 ) / n len = (K - 1) / n len=(K1)/n
偏移量: a d d = ( K − 1 ) m o d    n add = (K - 1) \mod n add=(K1)modn
a n s = a d d ∗ D ans = add * D % N + len ans=addD

最初容易想到特殊情况, 当 D D D N N N 的因子时,前 N / D N / D N/D 步肯定没有重复,接下来每一步都是上一轮同样倍数 + 1 1 1
若为 0 , 3 , 6 , 9 … … 0,3,6,9…… 0,3,6,9…… 下一轮即为 1 , 4 , 7 , 10 … … 1,4,7,10…… 1,4,7,10……
随后再引申到非因子的数应该也有一个循环节上进行考虑。

代码

#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
inline int gcd(int a, int b){
    if(b) while( (a %= b) && (b %= a));
    return a + b;
}
void solve()
{
    ll N, K, D;
    cin >> N >> D >> K; 
    ll g = gcd(N, D);
    ll n = N / g;
    ll len = (K - 1) / n;
    ll add = (K - 1) % n; 
    cout << add * D % N + len << "\n";
    return ;  
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)solve();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值