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
3
Sample Output
样例输出一
2
2
HINT
对于样例一,1*1*2=2;
数据范围与约定
对于 100% 的数据,N≤10^15
Source
思路:现在我们将问题转化成,找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\n",ans); return 0; }