题目大意
给出一个长度为
2n−1
2
n
−
1
的序列,其中第
2i−1
2
i
−
1
个位置上的数是
i
i
。
每次把最后一个数往前跳到第一个遇到的空格上,直到最后n个数都在前n个格子为止。
给出p组询问,求最终序列的某个位置上的数是多少。
比如1 2 3 4 会变成 1324
(CF上的题翻译很egg ache啊)
题解
这是我最后半分钟写出的奇妙解法
通过找规律发现
比如当n=6时,最终数组为1 4 2 6 3 5
显然奇数位是
而把偶数位提出来后
4 6 5 ???
减3后
1 3 2
欸这不就是n=3的情况吗
这是n为偶数的情况,再比如n=7
最终数组为1 6 2 5 3 7 4
提出偶数位
6 5 7
减4
2 1 3
可以显然地发现,实际就是把n=3的情况的最后一位移到第一位
那么实际求就往前移一位
证明
当n为偶数时:
a1 a2 a3 a4…an (n%2==0)
既然是按顺序往前跳,那么前n/2个肯定是不变的
假设先把后n/2个跳到一起,结果就是n/2的方案+n/2
这时前面刚好有n/2个空格,所以可以依次跳到前面的空格里,而顺序不变。
当n为奇数时:
a1 a2 a3 a4…an (n%2==1)
按顺序往前跳,前(n+1)/2个不变
后(n-1)/2就是(n-1)/2的答案+(n+1)/2
然而此时前面有(n+1)/2个空格,但只有(n-1)/2个数,所以跳完一轮后最后一个数会跳到最前
于是就这样乱搞就行了
(似乎没有人跟我方法一样那么就算作原创吧)
code
(CF上不能用%lld)
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;
long long n,Q,s,i;
long long get(long long t,long long s)
{
if (s&1) return (s+1)>>1;
if (t&1)
{
if ((s>>1)==1)
return get(t>>1,t>>1)+(t>>1)+1;
else
return get(t>>1,(s>>1)-1)+(t>>1)+1;
}
else
return get(t>>1,s>>1)+(t>>1);
}
int main()
{
scanf("%I64d%I64d",&n,&Q);
for (;Q;Q--)
{
scanf("%I64d",&s);
printf("%I64d\n",get(n,s));
}
}