数位dp
总结一下规律
数位dp的题面大多是对数进行了一些限制,然后让你求这种数的个数。
解题有如下步骤:
1.根据题目的要求,初始化符合要求的数,用数组存下。
2.获取,题目一般会给你一个范围,考虑前缀和的思想
例如[x,y],可以转换为[1,y+1)-[1,x)
注意你的获取函数大多都是左开右闭的
3.首先位数不足的先统计
再从高到低位进行比较,假设这一位比原来数的这一位少1,设这一位为i,那你后面就可以随便取了,所以贡献就是 10^(i-1)
最后把当前情况的数量乘以贡献求和就是答案
一道稍微难一点的例题
花神的数论题
#include<bits/stdc++.h>
typedef long long ll;
const int mod=10000007;
using namespace std;
ll f[100][100],n;
ll cnt=0,input[100];
ll qpow(ll x,ll y){
ll ans=1;
while(y){
if(y&1) ans=(ans*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return ans;
}
void init(){
for(int i=0;i<=60;i++){//log(1e15)
f[i][0]=1;
}
for(int i=1;i<=60;i++){
for(int j=1;j<=i;j++){
f[i][j]=f[i-1][j-1]+f[i-1][j];
}
}
}
void cal(){//计算n的二进制位数
// ll m=n+1;
while(n){
input[++cnt]=n&1;
n>>=1;
}
}
ll get(ll x){//统计二进制分解下有j个1的数,其贡献和为j^sum
ll sum=0;
for(int i=cnt;i>0;i--)//首->尾
{
if(input[i]) sum+=f[i-1][x--];//如果这一位为1,考虑在这一位放1,在它的后面放x-1个就够了
if(x<0) break;
}
return sum;
}
ll ans=1;
int main(){
cin>>n;
n++;
cal();
init();
// cout<<cnt<<endl;
for(int i=1;i<=cnt;i++){
ans=(ans*qpow(i,get(i))%mod)%mod;
}
cout<<ans<<endl;
}