Codeforces Round #469 (Div. 2) ——D. A Leapfrog in the Array

题意:1~n的数按顺序间隔排列,(1  2  3  4  ...  n) 中间的空格也要占一位,现在有一种操作,每次把序列最后面那个数移到离它最近的空格里面,直到前n个位置都被填满,没有空格了。给出q次查询,每次输入一个数 Xi,要求输出按照所给操作完成后的Xi位置上的数是多少。

题解:题目上给的数据范围超级大(1e18),所以肯定是不能模拟的,一定有什么规律在里面。首先很容易发现,不是根本不用发现,我们就知道一开始所有的数字使在奇数下标的位置上,每次我们移动一个数都是移动到一个偶数位置上,同时前n个数中,奇数位置上的数是不会动的,因为后面的数填空都是填到偶数下标位置上,而填到第n个数的时候前面的空位科宁已经被填满了,他们会一直保持在奇数位置上,所以当查询的位置是奇数时,我们可以直接输出原位置上的数,而这个数是非常好得到的(原来每个数是空一个放一个,那数x的下标就是(2x-1),所以知道下标Xi,求位置上的数就是(Xi+1)/2)。而当查询的是偶数位置上的数时,我们假设当前这个偶数位置是p,那么这个位置前面一定有p/2个数,它后面就有(n-p/2),包括当前位置上的数,所以这个数是从p+n-p/2的位置跳到当前这个位置上的(体会一下,既然能跳到这个位置上,那一定是后面每空格了全是数,所以才会跳到这里),那我们只用一直向后推,推到什么时候才能得到它最初的位置呢?答案就是当到了一个奇数位置的时候,因为我们已经讲过所有的数一开始就是在奇数位置上,而他们每次跳都会跳到偶数位置上。得到了最初位置就可以用刚刚我们推出来的用位置下标算数的大小的式子计算了。

附上代码(非常简短易懂 ):

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    ll n,x;
    int q;
    scanf("%I64d%d",&n,&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%I64d",&x);
        long long num;
        if(x&1)//奇数位置直接算
            num=(x+1)/2;
        else {
                while(x%2==0)//向后推这个位置上的数的最初位置
                 x+=(n-x/2);
            num=(x+1)/2;
        }
        printf("%I64d\n",num);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wookaikaiko/article/details/81070332