BZOJ 3209: 花神的数论题 数位DP

来源:互联网 发布:微盘交易平台源码出售 编辑:程序博客网 时间:2024/06/05 09:58

Description

背景
众所周知,花神多年来凭借无边的神力狂虐各大 OJ、OI、CF、TC …… 当然也包括 CH 啦。
描述
话说花神这天又来讲课了。课后照例有超级难的神题啦…… 我等蒟蒻又遭殃了。
花神的题目是这样的
设 sum(i) 表示 i 的二进制表示中 1 的个数。给出一个正整数 N ,花神要问你
派(Sum(i)),也就是 sum(1)—sum(N) 的乘积。

Input

一个正整数 N。

Output

一个数,答案模 10000007 的值。

Sample Input

样例输入一

3

Sample Output

样例输出一

2

HINT

对于样例一,1*1*2=2;

数据范围与约定

对于 100% 的数据,N≤10^15

题解

挺傻的一道数位DP,f[60][60][2]代表到达第i位,二进制中有j个1,是否顶位的方案数,最后快速幂乘一下即可。

#include<cstdio>#include<cstdlib>#include<iostream>#include<iomanip>#include<cstring>#include<ctime>#include<cmath>#include<cstring>#include<string>using namespace std;long long f[70][70][2];const int mod=1e7+7;long long ksm(long long x,long long t){    long long re=1;    while(t)    {        if(t&1) re=re*x%mod;        x=x*x%mod;        t>>=1;    }    return re;}void dp(long long x){    f[0][0][1]=1;    for(int i=0;i<=60;i++)        for(int j=0;j<=60;j++)            for(int k=0;k<=1;k++)            {                if(!f[i][j][k]) continue;                for(int kk=0;kk<=1;kk++)                {                    if(k)                    {                        if(!(x&(1ll<<(60-i))))                        {                            if(kk==1) continue;                            f[i+1][j+kk][1]+=f[i][j][k];                        }                        else                        {                            if(kk==0) f[i+1][j+kk][0]+=f[i][j][k];                            else f[i+1][j+kk][1]+=f[i][j][k];                        }                    }                    else f[i+1][j+kk][k]+=f[i][j][k];                }            }    long long ans=1;    for(int i=1;i<=60;i++)    {        ans*=ksm(i,f[61][i][0]+f[61][i][1]);        ans%=mod;    }    cout<<ans<<endl;}int main(){    long long x;    scanf("%lld",&x);    dp(x);    return 0;}