https://nanti.jisuanke.com/t/41352
大概就是约瑟夫环的亚子。
比如输入n,M,q
询问从上到下第x张牌的数字是啥
1肯定是第1个拿走的,特判
剩下就是n-1个牌,x就变成第x-1张牌,如果下标从0开始标号,就变成了x-2
假设res=n-1个人中按M+1报数下标为x-2个人是在第几轮死,则res+1就是要的答案,加的这个1是最顶端的牌
自己赛后才磨出模拟的过程,然后发现学弟写得递归版本超优秀的QWQ
报的数字比较小的时候跑的飞快,最差是O(n)吧因为每次while循环至少减掉1个人。
以下是我的码子……
#include
using namespace std;
typedef long long ll;
const ll N=4e7+7;
ll n,k;
int main(){
ll t,x,q;
scanf("%lld",&t);
while(t--){
scanf("%lld%lld%lld",&n,&k,&q);
n--;k++;
ll N=n;
while(q--){
n=N;
ll ans=0;
scanf("%lld",&x);
if(x==1){
printf("1\n");continue;
}
x-=2;
ll tmp=0;
while(n){
x=(x+n)%n;
if(k>n)x+=(k-x-1+n-1)/n*n;
if((x+1)%k==0){
tmp+=(x+1)/k;break;
}else{
if(k>n){
tmp+=x/k;
ll ttmp=x;
x=x-(x/n+1)*(x/k)+(x+n)/n*n-k;
n-=ttmp/k;
}else{
tmp+=n/k;
x=x-x/k;
x+=n-n/k*k;
n-=n/k;
}
}
}
printf("%lld\n",tmp+1);
}
}
}
然后是学弟写的非递归版本,机智炸了。
#include
using namespace std;
typedef long long ll;
const int N=4e7+7;
int f(int n, int k, int m, int M)
{
if (n == 1)return 1;
int at = ((k+(m?m:M)) / M) ;
if ((k + m) % M == 0)return at;
int cnt = (n+(m?m:M)-1)/M;
return cnt + f(n - cnt, k - at, (n + m) % M, M);
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
int n, m, q, k;
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= q; i++)
{
scanf("%d", &k);
printf("%d\n", f(n, k - 1, 0, m+1));
}
}
}
这是特地整理出来求下标x的人第几轮死的函数,我觉得大概没有整理错,还没用它A过题
对自己佩服炸了,写出了一个对我来说很复杂的过程。
#include
using namespace std;
typedef long long ll;
const ll N=4e7+7;
ll n,k;//可做n<=4e7,询问个数<=100,下标范围[0,n-1]
ll dieInXturn(int n,int k,int x){//n个人,报数k,下标为X的人第几个死亡
ll tmp=0;
while(n){
x=(x+n)%n;
if(k>n)x+=(k-x-1+n-1)/n*n;
if((x+1)%k==0){
tmp+=(x+1)/k;break;
}else{
if(k>n){
tmp+=x/k;
ll ttmp=x;
x=x-(x/n+1)*(x/k)+(x+n)/n*n-k;
n-=ttmp/k;
}else{
tmp+=n/k;
x=x-x/k;
x+=n-n/k*k;
n-=n/k;
}
}
}
return tmp;
}
int main(){
ll t,x,q;
scanf("%lld",&t);
while(t--){
scanf("%lld%lld%lld",&n,&k,&x);
printf("%lld\n",dieInXturn(n,k,x));
}
}