2069: Saruman’s Level Up
Submit Page Summary Time Limit: 5 Sec Memory Limit: 512 Mb Submitted:176 Solved:32Description
Saruman’s army of orcs and other dark minions continuously mine and harvest lumber out of the land surrounding his mighty tower for N continuous days. On day number i, Saruman either chooses to spend resources on mining coal and harvesting more lumber, or on raising the level (i.e., height) of his tower. He levels up his tower by one unit only on days where the binary representation of i contains a total number of 1’s that is an exact multiple of 3. Assume that the initial level of his tower on day 0 is zero. For example, Saruman will level up his tower on day 7 (binary 111), next on day 11 (binary 1011) and then day 13, day 14, day 19, and so on. Saruman would like to forecast the level of his tower after N days. Can you write a program to help?
Input
The input file will contain multiple input test cases, each on a single line. Each test case consists of a positive integer N < 1016, as described above. The input ends on end of file.
Output
For each test case, output one line: “Day N: Level = L”, where N is the input N, and L is the number of levels after N days.
Sample Input
2 19 64
Sample Output
Day 2: Level = 0 Day 19: Level = 5 Day 64: Level = 21
Hint
Source
pacnw2012
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
#include<map>
#define N 100010
#define LL long long
using namespace std;
LL dp[64][64][2],num[64];
LL dfs(int len,int x,bool lim,bool t)
{
if (x>len) return 0;
if (len==0) return x==0;
if (!lim&&dp[len][x][t]) return dp[len][x][t];
int up=lim?num[len]:1;
LL res=0;
for(int i=0;i<=up;i++)
res+=dfs(len-1,x-(i==t),lim&&up==i,t);
if (!lim) dp[len][x][t]=res;
return res;
}
LL cal(LL x)
{
int tot=0;
while(x)
{
num[++tot]=x&1;
x/=2;
}
LL res=0;
for(int i=3;i<=tot;i+=3)
{
int r=tot-i;
if (i>r) res+=dfs(tot,i,1,1);
else res+=dfs(tot,r,1,0);
}
return res;
}
int main()
{
LL n;
memset(dp,0,sizeof(dp));
while(~scanf("%lld",&n))
{
printf("Day %lld: Level = %lld\n",n,cal(n));
}
}
此外,还可以考虑用组合数学的方法计算。对于一个长度为len的二进制数字从最右边开始标号,考虑枚举一个二进制数位i,可以提前预处理出i+1~len的1的个数s,然后枚举前面的i位中1的个数,用组合的方法计算 Σc(i,j),其中j从0到i-1(因为最高位为0才能使前面任意取),且保证(s+j)%3==0,当然要保证s+j>0。具体见代码:
#include<cstdio>
#define LL long long
#define N 100010
using namespace std;
LL c[61][61],s[61],n,tot,ans;
void init()
{
c[0][0]=1;
for(int i=1;i<=60;i++)
for(int j=0;j<=i;j++)
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
int main()
{
init();
while(~scanf("%lld",&n))
{
ans=tot=0;
printf("Day %lld: Level = ",n);
while(n)
{
s[++tot]=n&1; n>>=1;
s[tot]+=s[tot-1];
}
for(int i=1;i<=tot;i++)
{
int last=s[tot]-s[i];
if (s[i]-s[i-1]==0) continue;
for(int j=0;j<i;j++)
{
if (j+last==0) continue;
if ((j+last)%3) continue;
ans+=c[i-1][j];
}
}
if (s[tot]%3==0) ans++;
printf("%lld\n",ans);
}
return 0;
}