首页 > 代码库 > BZOJ 3209: 花神的数论题【数位dp】

BZOJ 3209: 花神的数论题【数位dp】

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,计算小于n并且sum(i)=k的i有多少个,设为u,则答案为pow(k,u),然后枚举k即可

 

#include<cstdio>

#include<iostream>

#include<cstring>

#include<map>

#define maxn 1000005

#define MOD 10000007

using namespace std;

long long num[maxn],h=0,dp[100][100][100][2];

long long dfs(long long pos,long long need,long long now,long long limit)

{

    if(pos==0)return now==need;

    int tmp=limit?num[pos]:1;

    long long ans=0;

    if(!limit&&dp[pos][need][now][limit]!=-1)

        return dp[pos][need][now][limit];

    for(int i=0;i<=tmp;i++)

    {

        ans=(ans+dfs(pos-1,need,now+i,limit&&(i==tmp)));

    }

    if (!limit)

        dp[pos][need][now][limit]=ans;

    return ans;

}

long long mpow(long long a,long long n)

{

    long long ans=1;

    a%=MOD;

    while (n)

    {

        if (n%2) ans=(ans%MOD)*(a%MOD)%MOD;

        n/=2;

        a=(a%MOD)*(a%MOD)%MOD;

    }

    return ans;

}

int main()

{

    long long n;

    memset(dp,-1,sizeof(dp));

    while(scanf("%lld",&n)!=EOF)

    {

        long long ans=1;h=0;

        if(n==0){printf("0\n");continue;}

        while(n>0){num[++h]=n&1;n>>=1;}

        for(int i=1;i<=h;i++)

        {

            long long u=dfs(h,i,0,1);

            long long v=mpow((long long)i,u%9988440+9988440);

            ans=((ans%MOD)*(v%MOD))%MOD;

            if(ans==6296768)

            {

                int zz=1;

            }

        }

        printf("%lld\n",ans);

    }

    return 0;

}

?

BZOJ 3209: 花神的数论题【数位dp】