[XXSY]数位DP专题 刷题笔记
Mistletoes
·
·
个人记录
数位DP专题
不要62
题目:
求区间[l,r]中有多少个数满足:不含4和62
题解:
ll dfs(int pos,bool limit,bool pre){
if(!pos) return 1;
if(!limit&&ans[pos][pre]!=-1) return ans[pos][pre];
int tp=limit?a[pos]:9;
ll res=0;
for(int i=0;i<=tp;i++){
if(i==4) continue;
if(pre&&i==2) continue;
res+=dfs(pos-1,limit&&(i==tp),i==6);
}
if(!limit) ans[pos][pre]=res;
return res;
}
F(x)
题目:
对于具有n位数 (A_nA_{n-1}A_{n-2}...A_2A_1)的十进制数x,我们将其权值定义F(x)=\sum_{k=1}^{n}A_k\times 2^{k-1}。现在给出两个数字A和B,请计算0和B之间有多少数字,其权值不超过F(A)。
题解:
int dfs(int len,int val,bool limit){
if(len==0) return val<=a;//a=F(A)
if(val>a) return 0;
if(!limit&&f[len][a-val]) return f[len][a-val];
int sum=0;
for(int i=0;i<=(limit?num[len]:9);i++)
sum+=dfs(len-1,val+i*(1<<len),limit&&i==num[len]);
if(!limit) f[len][a-val]=sum;
return sum;
}
Bomb
题目:
给一个数字N,求1~N有多少个数字的序列包含“49”子序列。
题解:
ll dfs(int len, int is, int limit){
if(len==0) return 1;
if(dp[len][is]&&!limit) return dp[len][is];
ll cnt=0;
int p=limit?num[len]:9;
for(int i=0;i<=p;i++){
if(is&&i==9) continue;
cnt+=dfs(len-1,i==4,i==p&&limit);
}
if(!limit) dp[len][is]=cnt;
return cnt;
}
Round Numbers
题目:
如果一个正整数N的二进制表示中,0的个数大于或等于1的个数,那么N就被称为”round number” 。求在一个给出的闭区间[l,r]中有多少个round numbers。
题解:
把l,r转成二进制,然后跑数位dp
开一个五维数组记忆化:f[d][z][o][q][e]。
其中d,z,o,q,e分别表示:当前搜到第d位,有z个0,o个1,q=0,1 表示前面是否全为0(是的话不计0的个数),e=0,1 表示是否已严格小于给定上限。
int dfs(int d,int z,int o,int q,int e){
if(!d) return z>=o;
if(f[d][z][o][q][e]!=-1)
return f[d][z][o][q][e];
if(a[d]){
f[d][z][o][q][e]=dfs(d-1,z,o+1,1,e);
f[d][z][o][q][e]+=dfs(d-1,z+q,o,q,1);
}
else{
f[d][z][o][q][e]=dfs(d-1,z+q,o,q,e);
if(e) f[d][z][o][q][e]+=dfs(d-1,z,o+1,1,e);
}
return f[d][z][o][q][e];
}
吉哥系列故事——恨7不成妻
题目:
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
- 整数中某一位是7;
- 整数的每一位加起来的和是7的整数倍;
- 这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
题解:
现有一个n位十进制数\overline{A_nA_{n-1}...A_{pos}XXX...X}(第n位到第pos位上的数已经确定为A_n,A_{n-1},...,A_{pos},其余位置的数还未确定)
设Y=\overline{A_nA_{n-1}...A_{pos}000...0},那么(\overline{A_nA_{n-1}...A_{pos}XXX...X})^2=(Y+\overline{XXX...X})^2=Y^2+(\overline{XXX...X})^2+2Y\overline{XXX...X}
那么数位dp时要开三个记忆化数组,分别维护 和7无关的数字的个数、和7无关的数字的和、和7无关的数字的平方和
B_number
题目:
若一个非负整数的十进制形式包含子串“13”,且是13的倍数,则该数为B数。对给定的n,求1~n中有几个B数。
题解:
数位dp裸题
[SCOI2009]windy数
题目:
题解:
```
ll dfs(int pos,int pre,int st,int limit)//pos当前位置,pre前一位数,st判断前面是否全是0,limit最高位限制
{
if(pos>len) return 1;//搜完了
if(!limit&&dp[pos][pre]!=-1) return dp[pos][pre];//没有最高位限制,已经搜过了
ll ret=0;
int res=limit?a[len-pos+1]:9;//当前位最大数字
for(int i=0;i<=res;i++)//从0枚举到最大数字
{
if(abs(i-pre)<2) continue;//不符合题意,继续
if(st&&i==0) ret+=dfs(pos+1,-2,1,limit&&i==res);//如果有前导0,下一位随意
else ret+=dfs(pos+1,i,0,limit&&i==res);//如果没有前导0,继续按部就班地搜
}
if(!limit&&!st) dp[pos][pre]=ret;//没有最高位限制且没有前导0时记录结果
return ret;
}
```
### 「一本通 5.3 例 1」Amount of Degrees
题目:
求给定区间$[X,Y]$中满足下列条件的整数个数:这个数恰好等于$K$个互不相等的$B$的整数次幂之和。
题解:
在B进制下做数位dp
```
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll l,r,k,b,mul[35],len,num[35],f[35][25];
inline ll dfs(ll pos,ll cnt,ll lim){
if(pos==len+1)return cnt==k;
if((!lim)&&~f[pos][cnt])return f[pos][cnt];
ll tmp=0,up=lim?num[pos]:1;//题目要求B的整数次幂互不相同,所以B进制下每一位最多是1
for(ll i=0;i<=up;++i)if(cnt+i<=k)tmp+=dfs(pos+1,cnt+i,(lim&&(i==up)));
if(!lim)f[pos][cnt]=tmp;
return tmp;
}
inline ll solve(ll x){
if(!x)return 0;
for(len=0;mul[len]<=x;++len);
--len,memset(f,-1,sizeof(f));
for(ll i=len;~i;--i){
if(x>=mul[i])x-=mul[i],num[i]=1;
else num[i]=0;
}
reverse(num,num+len+1);
return dfs(0,0,1);
}
int main(){
cin>>l>>r>>k>>b,mul[0]=1;
for(ll i=1;mul[i-1]<=r;++i)mul[i]=mul[i-1]*b;
cout<<solve(r)-solve(l-1);
return 0;
}
```
### 「一本通 5.3 例 2」数字游戏-不降数
题目:
某人命名了一种不降数,这种数字必须满足从左到右各位数字成小于等于的关系。现在指定一个整数闭区间$[a,b]$,问这个区间内有多少个不降数。
题解:
数位dp,记录一下上一位的数字、判一下前导零即可
### 「一本通 5.3 练习 1」数字游戏
题目:
某人命名了一种取模数,这种数字必须满足各位数字之和$\mod N$为$0$。指定一个整数闭区间$[a,b]$,问这个区间内有多少个取模数。
题解:
数位dp,记录一下目前为止各位数字之和模$N$的余数
```
int dfs(int len,int sum,int limit) {
if(len==0&&sum==0) return true;
else if(len==0) return false;
if(!limit&&(~dp[len][sum])) return dp[len][sum];
int up=9;
if(limit) up=c[len];
int ans=0;
for(int i=0;i<=up;i++) {
ans+=dfs(len-1,(sum+i)%n,limit&&(i==c[len]));
}
if(!limit) dp[len][sum]=ans;
return ans;
}
```