bzoj 3209: 花神的数论题 数位dp

3209: 花神的数论题

Time Limit: 10 Sec  Memory Limit: 128 MB
[Submit][Status][Discuss]

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

Source

原创 Memphis

思路:现在我们将问题转化成,找1-r区间内二进制数为i的个数,这样就是简单的数位dp了,然后求出个数之后,只需要快速幂一下即可;

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<queue>
#include<algorithm>
#include<stack>
#include<cstring>
#include<vector>
#include<list>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define pi (4*atan(1.0))
#define eps 1e-4
#define bug(x)  cout<<"bug"<<x<<endl;
const int N=1e5+10,M=1e6+10,inf=2147483647;
const ll INF=1e18+10,mod=2147493647;
ll f[60][60],bit[60];
ll dp(int pos,int sum,int p,int flag)
{
    if(pos==0)return (sum==p);
    if(flag&&f[pos][sum]!=-1)return f[pos][sum];
    int x=flag?1:bit[pos];
    ll ans=0;
    for(int i=0;i<=x;i++)
    {
        ans+=dp(pos-1,sum+i,p,flag||i<x);
    }
    if(flag)f[pos][sum]=ans;
    return ans;
}
ll getans(ll x,int p)
{
    int len=0;
    while(x)
    {
        bit[++len]=x%2;
        x/=2;
    }
    return dp(len,0,p,0);
}
ll quick(ll a,ll b,ll c)
{
    ll ans=1;
    while(b)
    {
        if(b&1)ans=(ans*a)%c;
        b>>=1;
        a=(a*a)%c;
    }
    return ans;
}
int main()
{
    ll r;
    scanf("%lld",&r);
    ll ans=1;
    for(int i=1;i<=60;i++)
    {
        memset(f,-1,sizeof(f));
        //cout<<i<<" "<<getans(r,i)<<endl;
        //memset(f,-1,sizeof(f));
        ans=(ans*quick(i,getans(r,i),10000007))%10000007;
    }
    printf("%lld
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/jhz033/p/6594343.html