题目地址:http://bestcoder.hdu.edu.cn/contests/contest_chineseproblem.php?cid=653&pid=1002
题目意思:(略)
题解:很容易发现,这个字符串最后会是1121223122323341223233423343445....
我们可以将其前缀有特定的规律:
1 长度1,sum值1
112 长度3,sum值4
1121223 长度7,sum值12
112122312232334 长度为15,sum值32
...
找到规律木有,长度=上一个长度*2+1。而sum值=上一个sum值*2+1+上一个长度值
那么我们可以初始化出长度小于1e16的这些特定数字(1,3,7,15...)的sum的值。
初始化之后就是计算了,比较难说清楚。
先假设求长度得函数为len()
比方:如果求长度7或者长度15,那么直接输出对应的sum值就可以了
那对于5这样不属于特定长度的数字,我们发现~~前三位的和长度3是一样的,那么先把长度3的值先存下来,之后来的一个数字肯定是1(复制中间隔的0+1产生),之后剩下的长度就剩1位了,此位的数字为len(1)+剩余的长度(1)
设小于等于n的特定数字的最大值为m
所以我们就推出了公式,len(n)=len(m)+1+len(n-m-1)+(n-m-1)
那么利用这个公式不断计算就可以了。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 510
using namespace std;
typedef long long ll;
ll len;
struct NODE
{
ll x,val;
}a[N];
int found(ll x)
{
ll i;
for(i=1;i<len;i++)
{
if(a[i].x>x)
return i-1;
}
return len-1;
}
int main()
{
ll i,T,k;
ll ans,tmp;
a[1].x=1;a[1].val=1;
for(i=2;i<=300;i++)
{
a[i].x=a[i-1].x*2+1;
a[i].val=a[i-1].val*2+1+a[i-1].x;
if(a[i].x>1e16)
break;
}
len=i;
scanf("%lld",&T);
while(T--)
{
ans=0;
scanf("%lld",&tmp);
while(tmp)
{
k=found(tmp);
tmp-=a[k].x;
if(tmp>=1)
{
tmp--;
ans++;
}
ans+=a[k].val+tmp;
}
printf("%lld\n",ans);
}
return 0;
}