题意: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;
}