http://codeforces.com/gym/101955/problem/K
2018沈阳区域赛的一个题,我可真是菜,wa了无数发。
目前遇到的约瑟夫的两个问题:
- n个人组成一个圈,开始从1喊道k。喊k的人出局。问最后剩的序号是几?f(n)=(f(n-1)+k)%n。f(n)表示的是n个人最后剩下的人的序号(0-n-1)
- 求第m个出局的人的序号。f(n,m)=( f(n-1,m-1) + k )%n (下表也是0-n-1)
这个题经典之处:因min(m,k)<=1e6。
- m<=k的时候f(n,m)可以由f(n-m+1,1)由O(n)的复杂度推过来
- m>k的时候,就要分块处理。这个挺巧妙的。
坑点:
- 慎用ceil
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
int t,Case=1;
ll n,m,k;
scanf("%d",&t);
while(t--){
scanf("%lld%lld%lld",&n,&m,&k);
printf("Case #%d: ",Case++);
if(m>n) m=n;
if(1==k){
printf("%lld\n",m);
continue;
}
ll ans=(k-1)%(n-m+1);
if(m<=k){
for(ll i=n-m+2;i<=n;i++){
ans=(ans+k)%i;
}
}else{
for(ll i=n-m+1;i<n;){
ll x=(i-ans)/(k-1);
if((i-ans)%(k-1)) x++;
if(0==x) x=1;
if(i+x>n) x=n-i;
ans=(ans+x*k%(i+x))%(i+x);
i+=x;
}
}
printf("%lld\n",ans+1);
}
return 0;
}