您的位置:首页 > 其它

BZOJ 3209 花神的数论题 数位dp

2017-10-16 14:54 381 查看

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


传送门
一开始看成了sigma(sum(1)……sum(n)),结果就随手上了= =
原来是乘积……突然不会
好吧其实还行,只要把问题转化掉就好了,
10^15约莫2^50,所以最多50个1,
枚举1的个数x,问题变成求出1~n内每个数的二进制含1个数恰好为x的个数,
假设这个个数为y,那么对答案的贡献就是x^y
怎么求呢……数位dp吧= =直接二进制的数位dp

一开始没加快速幂而10^8 TLE了好久……
我还以为是记忆化错了呢= =气死

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll
Mod=10000007;
ll n,f[55][55];
int len,a[55];
ll dfs(int now,bool limit,int num){
if (num<0) return 0;
if (now>len) return num==0;
if (!limit && f[now][num]>=0) return f[now][num];

ll t=dfs(now+1,limit&(a[now]==0),num);
if (!limit || a[now]==1) t+=dfs(now+1,limit,num-1);

if (!limit) f[now][num]=t;
return t;
}
ll ksm(ll a,ll b){
ll ans=1LL;a%=Mod;
while (b){
if (b&1LL) ans=ans*a%Mod;
b>>=1LL;
a=a*a%Mod;
}
return ans;
}
int main(){
scanf("%lld",&n);
len=0;
while (n) a[++len]=n&1LL,n>>=1LL;
for (int i=1;i<=(len>>1);i++) swap(a[i],a[len-i+1]);
memset(f,255,sizeof(f));

ll ans=1LL;
for (int i=1;i<=len;i++){
ll t=ksm(i,dfs(1,1,i));
ans=ans*t%Mod;
}
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: