Jzoj5421 嘟嘟噜
由于众所周知的原因, 冈部一直欠真由理一串香蕉.
为了封上真由理的嘴, 冈部承诺只要真由理回答出这个问题, 就给她买一车的香蕉:
一开始有n 个人围成一个圈, 从1 开始顺时针报数, 报出m 的人被机关处决. 然后下一个人再从1 开始报数, 直到只剩下一个人.
红莉栖: “这不就是约瑟夫问题吗...”
伦太郎: “助手你给我闭嘴!”
真由理虽然已经晕头转向了, 但听到有一车的香蕉, 两眼便放出了光芒.
为了封上真由理的嘴, 冈部承诺只要真由理回答出这个问题, 就给她买一车的香蕉:
一开始有n 个人围成一个圈, 从1 开始顺时针报数, 报出m 的人被机关处决. 然后下一个人再从1 开始报数, 直到只剩下一个人.
红莉栖: “这不就是约瑟夫问题吗...”
伦太郎: “助手你给我闭嘴!”
真由理虽然已经晕头转向了, 但听到有一车的香蕉, 两眼便放出了光芒.
约瑟夫问题:10^9版,m<=10^5
我们知道有一个O(n)的算法:s=(s+m)%i {i=1~n}
然而这样只能70分,我们需要改进算法
我们发现当i大于m的时候,其实有些步骤是可以合并为一步的,这个和反演里面用的那个除法很像
那我们根据当前i-s%i可以得出,接下来(i-s%i)/m步是可以合为一步走的,在接下来这些过程中,取模不会改变s+m的值
让后就可以乱搞,复杂度O(玄学)≈O(√n)
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m,s=0,T;
int main(){
freopen("mayuri.in","r",stdin);
freopen("mayuri.out","w",stdout);
for(scanf("%d",&T);T--;){
s=0;
scanf("%d%d",&n,&m);
// for(int i=2;i<=n;++i) s=(s+m)%i;
for(int i=2,p;i<=n;){
p=(i-s-1)/m+1;
if(i+p>n) p=n-i+1;
s=(s+m*p)%(i+p-1);
i=i+p;
}
printf("%d\n",++s);
}
}