51nod 1710 复杂度分析2
原题链接:
https://www.51nod.com/onlineJudge/questionCode.html#problemId=1710¬iceId=352908
定义:
low(n)
为
n
的二进制形式最低位1 以及比它更低位的
0
组成的二进制数。
特别的: low(0)=0
定义 f(i,j) 有:
当
i>j
时:
f(i,j)=0
当
i==j
时:
f(i,j)=1
当
i<j
且
low(j)≤j−i+1
:
f(i,j)=f(i,j−low(j))+1
当
i<j
且
low(j)>j−i+1
:
f(i,j)=f(i,j−1)+1
<script type="math/tex; mode=display" id="MathJax-Element-17"></script>
给定
n
,题目要求计算:g(n)=∑i=1n∑j=inf(i,j)
令:
a(n)=∑i=1nf(i,n)
令
C(k)
表示
[1,k]
所有数字二进制形式中,
1
的总个数, 这是一个经典的数位DP问题。
则有:
g(n)=∑i=1n∑j=inf(i,j)=g(n−low(n))+∑i=n−low(n)+1n∑j=inf(i,j)+∑i=1n−low(n)∑j=n−low(n)+1nf(i,j)
对于:
∑i=n−low(n)+1n∑j=inf(i,j)=g(low(n))
这是因为,
j
每次求出一段小区间。j 都会减小。但
j
始终大于等于n−low(n)
<script type="math/tex; mode=display" id="MathJax-Element-31"></script>
对于:
∑i=1n−low(n)∑j=n−low(n)+1nf(i,j)
这里的 f(i,j)=f(i,n−low(n))+f(n−low(n)+1,j)
其中 0<i≤n−low(n)<j
它的贡献分为两部分。第一部分,
f(i,n−low(n))
:
∑i=1n−low(n)∑j=n−low(n)+1nf(i,n−low(n))=low(n)a(n−low(n))
第二部分:
因为:f(n−low(n)+1,j)=f(1,j−(n+low(n)) )所以:∑i=1n−low(n)∑j=n−low(n)+1nf(1,j−(n+low(n)) )=C(low(n))(n−low(n))
综上:
g(n)=g(n−low(n))+g(low(n))+C(low(n))(n−low(n))+low(n)a(n−low(n))
使用上面的技巧。为们还可以得到:
a(2k)=2a(2k−1)+k2k−1−1
g(2k)=2g(2k−1)+k−1+2k−1a(2k−1)+2k−1(C(2k−1)−1)+(2k−1−1)k
递推计算答案。
下面是代码:
#include <algorithm>
#include <string.h>
#include <stdio.h>
#define MAXN 5000
using namespace std;
typedef long long LL;
const int P=1e9+7;
int S[1010];
int N[MAXN];
int Pow[MAXN];
int A[MAXN];
int G[MAXN];
int C[MAXN];
int g[MAXN];
int a[MAXN];
int D[MAXN];
int read()
{
int len=0;
char c=getchar();
while(c>='0'&&c<='9')
{
S[len++]=c-'0';
c=getchar();
}
return len;
}
void init()
{
Pow[0]=A[0]=G[0]=C[0]=1;
for(int i=1 ;i<MAXN ;i++) Pow[i]=2ll*Pow[i-1]%P;
for(int i=1 ;i<MAXN ;i++) C[i]=(2ll*C[i-1]+Pow[i-1]-1)%P;
for(int i=1 ;i<MAXN ;i++) A[i]=(2ll*A[i-1]+(LL)i*Pow[i-1]-1)%P;
for(int i=1 ;i<MAXN ;i++)
{
G[i]=(G[i-1]<<1);
if(G[i]>=P)G[i]-=P;
G[i]+=i-1;
if(G[i]>=P)G[i]-=P;
G[i]+=(LL)A[i-1]*Pow[i-1]%P;
if(G[i]>=P)G[i]-=P;
G[i]+=(LL)Pow[i-1]*((C[i-1]+P-1)%P)%P;
if(G[i]>=P)G[i]-=P;
G[i]+=(LL)(Pow[i-1]-1)*i%P;
if(G[i]>=P)G[i]-=P;
}
}
int main ()
{
int n=read()-1,len=0;
for(int i=0,j=n;i<j;i++,j--) swap(S[i],S[j]);
while(S[n]==0&&n>=0)n--;
while(n>=0)
{
int c=0;
for(int i=n;i>=0;i--)
{
S[i]+=10*c;
c=S[i]&1;
S[i]>>=1;
}
while(S[n]==0&&n>=0)n--;
N[len++]=c;
}
init();
for(int i=len-1;i>=0;i--)
{
if(N[i]) D[i]=(D[i+1]+Pow[i])%P;
else D[i]=D[i+1];
}
for(int i=len-1;i>=0;i--)
{
if(N[i])
{
a[i]=a[i+1]+D[i+1];
if(a[i]>=P)a[i]-=P;
a[i]+=A[i];
if(a[i]>=P)a[i]-=P;
}
else a[i]=a[i+1];
}
for(int i=len-1;i>=0;i--)
{
if(!N[i]) g[i]=g[i+1];
else
{
g[i]=g[i+1]+G[i];
if(g[i]>=P)g[i]-=P;
g[i]+=(LL)Pow[i]*a[i+1]%P;
if(g[i]>=P)g[i]-=P;
g[i]+=(LL)D[i+1]*C[i]%P;
if(g[i]>=P)g[i]-=P;
}
}
printf("%d\n",g[0]);
return 0;
}