1. 程式人生 > >Gym 101864 A Criminal (約瑟夫環)

Gym 101864 A Criminal (約瑟夫環)

題意:t個樣例,m個人圍成一圈,m的範圍在l到n,一二報數,報數到二的人離開,直至剩下一個人,現在求編號為x的人留下的概率

思路:約瑟夫環有遞推公式,f[1] = 0;當一個人的時候,出隊人員編號為0,這裡編號從0開始,題目是從1開始,f[n] = (f[n-1] + m)%n ,m表示每次數到該數的人出列,n表示當前序列的總人數,一開始按照這個規律寫,程式碼很麻煩,一直沒有對,後來知道了當m為2的時候,有結論。當人數sum=2^k+t的時候,留下來的人的編號為2t+1(編號從1開始),所以這一題中,t=(x-1)/2,那麼只需要知道幾個k使得人數sum符合【l,n】,就是答案,要注意的是,當編號為偶數的時候是不可能被留下的,以及當編號小於人數的時候,預設被留下,特判即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll gcd(ll x,ll y){
    return y?gcd(y,x%y):x;
}
int main(){
    int t;
    scanf("%d",&t);
    int c=1;
    while(t--){
        ll x,l,n,len;
        ll ans=0;
        scanf("%lld%lld%lld",&x,&l,&n);
        printf("Case %d: ",c++);
        len=n-l+1;
        if(x>l){
            ans+=x-l;
            l=x;
        }
        if(x%2==0){//偶數情況先特判掉
            if(ans==0)printf("0/1\n");
            else{
                printf("%lld/%lld\n",ans/gcd(ans,len),len/gcd(ans,len));
            }
            continue;
        }
        ll a=(x-1)/2;
        ll sum=1;
        for(int i=0;i<55;i++){
            if(sum+a>=l&&sum+a<=n)ans++;
            sum=sum*2;
        }
        printf("%lld/%lld\n",ans/gcd(ans,len),len/gcd(ans,len));
    }
    return 0;
}