题目描述
Chiaki is interested in an infinite sequence a1, a2, a3, ..., which defined as follows:
Chiaki would like to know the sum of the first n terms of the sequence, i.e. . As this number may be very large, Chiaki is only interested in its remainder modulo (109 + 7).
输入描述:
There are multiple test cases. The first line of input contains an integer T (1 ≤ T ≤ 105), indicating the number of test cases. For each test case: The first line contains an integer n (1 ≤ n ≤ 1018).
输出描述:
For each test case, output an integer denoting the answer.
题意:求上面的公式的绝对值的前缀和
打表可以看出a[i] = (i的2进制表示下:相邻位相同的个数 - 相邻位不同的个数),例如a[13(1101)] = 1-2 = -1
然后就可以求数位DP了,因为i的2进制最高只有64位,所以a[i]的取值范围大概是[-64, 64],可以考虑暴力每个值各出现多少次
预处理sum[x][y][z]表示当前暴力到第x位,且第x位为y,有多少个数满足a[]值为z(包含前导0)
例如sum[5][1][-3]就表示刚好有5位,且最高位为1,且满足a[k]为-3的二进制k的个数,有转移
for(i=2;i<=66;i++)
{
for(j=1;j<=135;j++) //因为数组下标不能为负数,所以需要整体移位:[-64,64]→[1,129]
{
sum[i][0][j] = (sum[i-1][0][j-1]+sum[i-1][1][j+1])%mod;
sum[i][1][j] = (sum[i-1][1][j-1]+sum[i-1][0][j+1])%mod;
}
}
之后对于每一个询问n,从n的最高位开始,如果当前位为1,那么很显然将这一位改成0之后后面所有的数字都可以01任取,与此同时你可以算出前面的(二进制相邻位相同的个数 - 相邻位不同的个数),这样只需要算上你预处理的sum[]就可以求出当前位对答案的贡献了,不过直接暴力sum[]还是会超时,所以还需要对sum再求一次前缀和
超时但好理解的程序:
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define LL long long
LL sum[88][2][222], a[88];
int main(void)
{
LL T, n, i, j, len, ans, now, p;
sum[1][0][100] = sum[1][1][100] = 1;
for(i=2;i<=66;i++)
{
for(j=1;j<=200;j++)
{
sum[i][0][j] = sum[i-1][0][j-1]+sum[i-1][1][j+1];
sum[i][1][j] = sum[i-1][1][j-1]+sum[i-1][0][j+1];
}
}
scanf("%lld", &T);
while(T--)
{
len = 0;
scanf("%lld", &n);
while(n)
{
a[++len] = n%2;
n /= 2;
}
ans = now = 0;
for(i=len;i>=1;i--)
{
p = 0;
if(i!=len && a[i]!=a[i+1]) p = -1;
else if(i!=len) p = 1;
if(a[i])
{
if(i!=len)
{
for(j=1;j<=200;j++)
ans += sum[i][0][j]*abs(j-100+now-p);
}
}
if(i!=len)
{
for(j=1;j<=200;j++)
ans += sum[i][1][j]*abs(j-100);
}
now += p;
}
ans += abs(now);
printf("%lld\n", ans);
}
return 0;
}
AC代码(附打表程序):
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define LL long long
#define mod 1000000007
LL sum[70][2][138], Q[70][2][138], a[88];
int main(void)
{
LL T, n, i, j, k, len, ans, now, p;
sum[1][0][65] = sum[1][1][65] = 1;
for(i=2;i<=66;i++)
{
for(j=1;j<=135;j++) //因为数组下标不能为负数,所以需要整体移位:[-64,64]→[1,129]
{
sum[i][0][j] = (sum[i-1][0][j-1]+sum[i-1][1][j+1])%mod;
sum[i][1][j] = (sum[i-1][1][j-1]+sum[i-1][0][j+1])%mod;
}
}
for(i=1;i<=66;i++)
{
for(k=1;k<=135;k++)
{
for(j=1;j<=135;j++)
{
Q[i][0][k] = (Q[i][0][k]+sum[i][0][j]*abs(j-k))%mod;
Q[i][1][k] = (Q[i][1][k]+sum[i][1][j]*abs(j-k))%mod;
}
}
}
scanf("%lld", &T);
while(T--)
{
len = 0;
scanf("%lld", &n);
while(n)
{
a[++len] = n%2;
n /= 2;
}
ans = now = 0;
for(i=len;i>=1;i--)
{
p = 0;
if(i!=len && a[i]!=a[i+1]) p = -1;
else if(i!=len) p = 1;
if(a[i])
{
if(i!=len)
ans = (ans+Q[i][0][65-now+p])%mod;//sum[i][0][j]*abs(j-65+now-p);
}
if(i!=len)
ans = (ans+Q[i][1][65])%mod;
now += p;
}
ans = (ans+abs(now))%mod;
printf("%lld\n", ans);
}
return 0;
}
/*#include<stdio.h>
#include<stdlib.h>
int F[10005], S[10005];
void Jud(int x)
{
if(x<=1)
printf("%d", x);
else
{
Jud(x/2);
printf("%d", x%2);
}
}
int main(void)
{
int i;
for(i=1;i<=9999;i++)
{
if(i==1)
F[i] = 0;
else
{
if(i%4==2 || i%4==1)
F[i] = F[i/2]-1;
else
F[i] = F[i/2]+1;
}
//printf("%-5d", i);
//Jud(i);
//printf(": %d\n", F[i]);
S[i] = abs(F[i])+S[i-1];
if(i<=15 || i==7788)
printf("%d\n", S[i]);
}
return 0;
}*/