题目链接https://codeforces.com/gym/101955/problem/K
题意
给出一个长度为 n n n的约瑟夫环,标号为 k k k的人被淘汰。问第 m m m个人淘汰的初始标号。
1 ≤ n , m , k ≤ 1 e 18 1\leq n,m,k \leq 1e18 1≤n,m,k≤1e18, ∑ m i n ( m , k ) ≤ 2 e 6 \sum{min(m,k)}\leq 2e6 ∑min(m,k)≤2e6
题解
初始标号如果以零开头的话,约瑟夫环有一个公式:
d
p
[
n
]
[
m
]
=
(
d
p
[
n
−
1
]
[
m
−
1
]
+
k
)
%
n
dp[n][m]=(dp[n-1][m-1]+k)\%n
dp[n][m]=(dp[n−1][m−1]+k)%n,其中
d
p
[
n
]
[
m
]
dp[n][m]
dp[n][m]表示以用有
n
n
n个人,第
m
m
m个人淘汰的初始标号。
很容易求得
d
p
[
n
−
m
+
1
]
[
1
]
dp[n-m+1][1]
dp[n−m+1][1],然后更新到
d
p
[
n
]
[
m
]
dp[n][m]
dp[n][m]即可,更新分几种情况
- 如果 m < k m<k m<k,直接暴力更新
- 如果 m > k m>k m>k,那么就代表 n n n相对于 k k k很大,可以算出取模内能加上多少个 k k k,然后转化成乘法。
- k = 1 k=1 k=1,特判一下
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2e6+7;
ll t,cs;
ll n,m,k;
int main(){
scanf("%lld",&t);
while(t--){
scanf("%lld%lld%lld",&n,&m,&k);
if(k==1){
printf("Case #%lld: %lld\n",++cs,m);
continue;
}
if(m<=k){
ll ans=(k-1)%(n-m+1);
for(ll i=2,j=n-m+2;i<=m;i++,j++){
ans=(ans+k)%j;
}
printf("Case #%lld: %lld\n",++cs,ans+1);
}
else{
ll len=n-m+1;
ll ans=(k-1)%(n-m+1);
m--;
while(m>0){
ll x=(len-ans)/(k-1);
if(m<=x){
ans=(ans+m*k)%(len+x);
m=0;
}
else{
ans=(ans+x*k)%(len+x);
ans=(ans+k)%(len+x+1);
m-=x+1;
len+=x+1;
}
}
printf("Case #%lld: %lld\n",++cs,ans+1);
}
}
}