题意:
给定一个数列 a:
- 当 \(n\le 2\) 时,a_n=na**n=n。
- 当 \(n>2\),且 n 是奇数时, \(a_n=2\times a_{n-1}\)。
- 当 \(n>2\),且 n 是偶数时,\(a_n=a_{n-1}+r_{n-1}\)。
其中 \(r_{n-1}= \operatorname{mex}(|a_i-a_j|)(1\le i\le j\le n-1)mex(S)\) 表示最小的不在 S集合里面的非负整数。
数列 a 的前若干项依次为:
1,2,4,8,16,21,42,51,102,112,224,235,470,486,972,990,19801,2,4,8,16,21,42,51,102,112,224,235,470,486,972,990,1980。
可以证明,对于任意正整数 xx,只存在唯一一对整数 (p,q)满足 \(x=a_p-a_q\),定义为 \(\operatorname{repr}(x)\)。
比如 \(\operatorname{repr}(17)=(6,3)\),\(\operatorname{repr}(18)=(16,15)\)。 现有 n 个询问,每次给定一个正整数 x,请求出 \(\operatorname{repr}(x)\)
分析:
第一眼,不会做/bushi
仔细观察题面,我们可以发现,偶数位置的数增长极快,因为\(a_{57}\)就已经超过1e9了。而且对于大小超过1e9的数,奇数位和前一项偶数位的差也大于1e9,所以57位之后的数,能组成题目要求的repe函数的只有偶数项和前一位奇数项。
所以我们可以暴力打出前57位能形成的数,对于查询,如果在前面57项里查到了就输出。否则的话,需要二分在表里找到最大的小于他的数,并得到他是在表里的第几个。因为r[]是递增的,那么如果当前x代表了一个r[i],也就意味着前面的x-1个数都已经被别的数对所代表了。又因为表里有num个,这是前57项里有的。那么x-num就是从57项后的第几个数对有这样一个差x。那么每一个数对2个数,又因为这样的计算包含了最后差等于x的那个数对,所以答案就是
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
map <int ,pair<int ,int > > s;
typedef map<int ,pair<int ,int > >::iterator it;
int t,n;
int a[100],b[10000],cnt;
void work()
{
a[1]=1,a[2]=2;
s[1]=make_pair(2,1);
for(n=3;;n++)
{
if(n&1) a[n]=a[n-1]*2;
else
{
for(int j=1;;j++)
{
if(!s.count(j))
{
a[n]=a[n-1]+j;
break;
}
}
}
for(int j=1;j<n;j++)
{
s[a[n]-a[j]]=make_pair(n,j);
}
if((!(n&1))&&a[n]>1e9) break;
}
for(it l=s.begin();l!=s.end();l++)
{
b[++cnt]=l->first;
}
scanf("%d",&t);
while(t--)
{
int x;
scanf("%d",&x);
it l=s.find(x);
if(l!=s.end())
{
printf("%d %d\n",l->second.first,l->second.second);
}
else
{
int y=lower_bound(b+1,b+cnt+1,x)-b-1;
printf("%d %d\n",n+(x-y)*2,n+(x-y)*2-1);
}
}
}
}
int main()
{
zzc::work();
return 0;
}