BZOJ3209 花神的数论题(数位dp)

【题解】

数位dp的思想 


 
 

枚举的是二进制数 先预处理出所有i位二进制数中,含j个1的数的个数,就是C(i,j)

然后就是从高位到低位,处理填0还是1的情况 

填0:之后i-1位随机填0/1

填1:紧接着的[n对应的二进制数该位为0]的位只能填0(否则超过n) 

注意该算法计数到的所有情况不含SUM(n)!因此读入时,n++

数位dp是不是基本都要预处理 = = 

#include<stdio.h>
#include<stdlib.h>
#define MOD 10000007
typedef unsigned long long ULL;
ULL c[65][65]={0};
ULL ksm(ULL a,ULL n)
{
    ULL ans;
    a%=MOD;
    if(n==0) return 1;
    if(n==1) return a;
    ans=ksm(a,n/2);
    ans=(ans*ans)%MOD;
    if(n%2==1) ans=(ans*a)%MOD;
    return ans;
}
int main()
{
    ULL n,t,ans=1;
    int i,j,k=0,len=0;
    scanf("%llu",&n);
    n++;
    for(i=0;i<64;i++)//预处理组合数 
    {
        c[i][0]=1;
        for(j=1;j<i;j++)
            c[i][j]=c[i-1][j-1]+c[i-1][j];
        c[i][i]=1;
    }
    for(t=n;t>0;t=t>>1)
        len++;
    for(i=len;i>=1;i--)
        if(n>>(i-1)&1)
        {
            for(j=0;j<i;j++)
                if(j+k!=0) ans=(ans*ksm(j+k,c[i-1][j]))%MOD;//若这一位填0(比它高的位数能填1的都已经填了1)
            k++;//若这一位填1(k:之前填了k个1)
        }
    printf("%llu",ans);
    return 0;
}


你可能感兴趣的:(二进制,数位dp,bzoj)