51nod 1710 复杂度分析2

51nod 1710 复杂度分析2

原题链接:
https://www.51nod.com/onlineJudge/questionCode.html#problemId=1710&noticeId=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)ji+1 :
f(i,j)=f(i,jlow(j))+1

i<j low(j)>ji+1 :
f(i,j)=f(i,j1)+1

<script type="math/tex; mode=display" id="MathJax-Element-17"></script>

给定 n ,题目要求计算:
g(n)=i=1nj=inf(i,j)

令:
a(n)=i=1nf(i,n)

C(k) 表示 [1,k] 所有数字二进制形式中, 1 的总个数, 这是一个经典的数位DP问题。

则有:
g(n)=i=1nj=inf(i,j)=g(nlow(n))+i=nlow(n)+1nj=inf(i,j)+i=1nlow(n)j=nlow(n)+1nf(i,j)

对于:
i=nlow(n)+1nj=inf(i,j)=g(low(n))

这是因为, j 每次求出一段小区间。j都会减小。但 j 始终大于等于nlow(n)

<script type="math/tex; mode=display" id="MathJax-Element-31"></script>

对于:
i=1nlow(n)j=nlow(n)+1nf(i,j)

这里的 f(i,j)=f(i,nlow(n))+f(nlow(n)+1,j)

其中  0<inlow(n)<j

它的贡献分为两部分。第一部分, f(i,nlow(n)) :
i=1nlow(n)j=nlow(n)+1nf(i,nlow(n))=low(n)a(nlow(n))

第二部分:
f(nlow(n)+1,j)=f(1,j(n+low(n)) )i=1nlow(n)j=nlow(n)+1nf(1,j(n+low(n)) )=C(low(n))(nlow(n))

综上:
g(n)=g(nlow(n))+g(low(n))+C(low(n))(nlow(n))+low(n)a(nlow(n))

使用上面的技巧。为们还可以得到:

a(2k)=2a(2k1)+k2k11

g(2k)=2g(2k1)+k1+2k1a(2k1)+2k1(C(2k1)1)+(2k11)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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值