hdu3089(约瑟夫的优化)

来源:互联网 发布:此谓知本 此谓知之至也 编辑:程序博客网 时间:2024/04/30 02:56

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3089

题目大意:一共n人。从1号开始,每k个人T掉。问最后的人。n超大。

解题思路

除去超大的n之外。就是个约瑟夫环的裸题。

约瑟夫环递推公式,n为人数,k为步长。

f(1)=0

f(n)=[f(n-1)+k]%i  i∈[2,n]

f(n)还要经过起始位置修正,设起始位置为s,即ans=[f(n)+s]%n。

基本约瑟夫环优化就是当k=1的时候,每次递推就是在+1,可以直接算出来快速跳过,f(n)=f(1)+n-1

 

当n超大的时候,可以照着这种思路快速简化递推过程。在递推后期,f(x)+k在很长的周期内<i,假设有m个周期,

那么这些周期合并后的结果相当于f(x)+m*k。可以快速跳过。条件限制是: f(x)+m*k<i+(m-1)

可以推出来:

当m=1时,条件限制: f(x)+k<i

当m=2是,条件限制: f(x+1)+k<i+1=f(x)+2*k<i+1

当m=m时,条件限制:f(x)+m*k<i+(m-1)

化简有m<(i-f(x)-1)/(k-1),若能整除,就是(i-f(x)-1)/(k-1)-1,否则就是(i-f(x)-1)/(k-1)直接取整。

这样,i+=m,f(x)+=m*k,快速跳过了中间过程。

若i+m>n,说明快速跳越界了,这时候可以直接算出f(n)=f(x)+(n-i-1)*m。


#include "cstdio"#define LL long longLL solve(LL n,LL k,LL s=1){    if(k==1) return (n-1+s)%n;    LL ans=0;    //ans=(ans+k)%i    for(LL i=2;i<=n;)    {        if(ans+k<i) //快速跳跃        {            LL leap;            if((i-ans-1)%(k-1)==0) leap=(i-ans-1)/(k-1)-1;            else leap=(i-ans-1)/(k-1);            if(i+leap>n) return ((ans+(n+1-i)*k)+s)%n;            i+=leap;            ans+=leap*k;        }        else        {            ans=(ans+k)%i;            i++;        }    }    return (ans+s)%n;}int main(){    //freopen("in.txt","r",stdin);    LL n,k;    while(scanf("%I64d%I64d",&n,&k)!=EOF)    {        LL ans=solve(n,k);        if(ans==0) printf("%I64d\n",n);        else printf("%I64d\n",ans);    }}


0 0
原创粉丝点击