题目链接
题意
有2*n-1个方格,在里面用(1,0,2,0,3......)的规律放入数字。每次操作将数字串末尾的数字移动到距离这个数字最近的数字前面的空格。操作可以用下列的图来演示:
之后又q组询问,问在进行操作结束后的数字串的下表Xi的值是多少
题解
看到题面的时候,气氛开始蕉♂躁不安。咳咳。因为n的范围有10^18这么大,自然不可能直接进行操作。想必这一串数字一定是有一定规律的。先手写打表程序来看一看随着n不断增大,数字串操作结束后的规律。
1: 1
2: 1 2
3: 1 3 2
4: 1 3 2 4
5: 1 5 2 4 3
6: 1 4 2 6 3 5
7: 1 6 2 5 3 7 4
8: 1 5 2 7 3 6 4 8
9: 1 9 2 6 3 8 4 7 5
10: 1 6 2 10 3 7 4 9 5 8
11: 1 9 2 7 3 11 4 8 5 10 6
12: 1 7 2 10 3 8 4 12 5 9 6 11
13: 1 12 2 8 3 11 4 9 5 13 6 10 7
14: 1 8 2 13 3 9 4 12 5 10 6 14 7 11
15: 1 12 2 9 3 14 4 10 5 13 6 11 7 15 8
16: 1 9 2 13 3 10 4 15 5 11 6 14 7 12 8 16
17: 1 17 2 10 3 14 4 11 5 16 6 12 7 15 8 13 9
18: 1 10 2 18 3 11 4 15 5 12 6 17 7 13 8 16 9 14
19: 1 15 2 11 3 19 4 12 5 16 6 13 7 18 8 14 9 17 10
20: 1 11 2 16 3 12 4 20 5 13 6 17 7 14 8 19 9 15 10 18
从这一串数字中我们可以发现,当下表为奇数时,对应的数字一定是(下标a+1)/2。但是对于偶数下标的数还是没有什么办法。进过进一步的观察,我们发现偶数下标的数字也有一定的规律。举个例子,n=20时下标为6的数字是12,非常玄学的是,n=19时,下标为4(6-2)的数字是11(12-1),n=18时下标为2(4-2)的数字是10(11-1)。按照这个规律,n=17是下表为0的数字应该是9,但是这个9不见了。9去哪里了呢?我们看看n=17时最后的数字是9。wow,how exciting!此时的9下标正好是奇数,成功了!?并没有。再举一个例子,n=20时候下标为4的数字16,按照这个规律寻找到在串末尾的数字是18行的末尾数字14,但是此时14的下标并不奇数,所以并没有什么用,必须继续按照这个规律寻找下去。找下去之后,在第9行的末尾数字5正好符合要求。这时候我们要寻找的偶数下表的数字的结论终于出来了。我们可以依此寻找这个数对应的下标,直到下标为偶数位置。在这里还要有一个优化,对应数字在末尾的行数可以直接用(当前的行数-当前数字下标/2)(think?),这样程序就不会T。具体的实现在代码中会给出。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<string>
#include<cstring>
#include<bitset>
#define LL long long
#define mod 1e9+7
#define INF 0x3f3f3f3f
using namespace std;
namespace FastIO {
template<typename tp> inline void read(tp &x) {
x=0; register char c=getchar(); register bool f=0;
for(;c<'0'||c>'9';f|=(c=='-'),c = getchar());
for(;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0',c = getchar());
if(f) x=-x;
}
template<typename tp> inline void write(tp x) {
if (x==0) return (void) (putchar('0'));
if (x<0) putchar('-'),x=-x;
int pr[20]; register int cnt=0;
for (;x;x/=10) pr[++cnt]=x%10;
while (cnt) putchar(pr[cnt--]+'0');
}
template<typename tp> inline void writeln(tp x) {
write(x);
putchar('\n');
}
}
using namespace FastIO;
LL n,c;
int q;
main(){
read(n),read(q);
while(q--){
read(c);
if(c%2) writeln((c+1)/2); else{
LL d=n;
while(!(c%2)){//此处就是直接跳转到下一个行数
c=d-c/2;
d=c;
}
writeln(n-c+(c+1)/2);//理解代码后用记事本打个草稿就出来了
}
}
}