[约瑟夫环]2019南昌ICPC网络赛 E magic card

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));
    }
}

 

你可能感兴趣的:(想法题,好题,数论)