題目描述:
題目分析:
(Steins Gate,給出題人點贊!好評!)這麼多次模擬賽第一次AK
我們把0~n-1這n個人排成一排:
0 1 2 3 … n-1
然後編號爲m%n-1的人會被機關處決,從編號爲m%n的人開始重新計數,假設m%n=4:
可以看出這一輪相對於下一輪編號增加了4(即m%n),所以下一輪編號爲k的人在這一輪編號爲(k+m%n)%n
設表示n個人中倖存者的編號,那麼有
當n<=106時可以O(n)遞推,但是n<=109就行不通了。
注意到題目中m<<n,而當m<n時m%n=m,所以可以嘗試先把前m項遞推求出來,然後式子就變爲了:
而m<<n,可以看出我們可以嘗試一次遞推多輪而不必管模。
假設我們當前推到了,需要找到使得成立的最小的,簡單化簡可以得到,於是可以直接令。
經過測試發現這樣的遞推方式跑的很快(於是我就不管複雜度什麼的了,和O(n)遞推拍一拍之後直接下一題 )
考完題解是這樣的:
真是有yali的風範(我TM上不去Wikipedia啊啊啊啊啊啊)
然後我找到一篇文章:約瑟夫問題(Josephus problem)的klog(n)解法
文章的末尾有複雜度分析(然而我並沒有看懂第一步是怎麼來的,於是就咕咕咕了 )
Code(代碼是按照從1開始編號的方式寫的,不如從0開始的簡潔,讀者可以自行嘗試):
#include<bits/stdc++.h>
using namespace std;
int T,n,m;
int main()
{
freopen("mayuri.in","r",stdin);
freopen("mayuri.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
if(m==1) {printf("%d\n",n);continue;}
int f=1;
for(int i=2;i<=min(n,m);i++) f=(f+(m-1)%i)%i+1;
for(int j=m,i;j<n;j=i){
i=min(1ll*n,(1ll*j*m-f)/(m-1)+1);
f=(f+1ll*(i-j)*m-1)%i+1;
}
printf("%d\n",f);
}
}