数位dp的题目一般会问,某个区间内,满足某种性质的数的个数。
- 利用前缀和,比如求区间[l,r]中的个数,转化成求[0,r]的个数 [0,l-1]的个数。
- 利用树的结构来考虑(按位分类讨论)
P8764 [蓝桥杯 2021 国 BC] 二进制问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include<bits/stdc++.h> using namespace std; const int N=70; long long f[N][N]; long long n,k; void init() { for(int i=0;i<N;i++) for(int j=0;j<=i;j++) if(!j) f[i][j]=1; else f[i][j]=f[i-1][j]+f[i-1][j-1]; }
long long dp(long long n) { if(!n) return 0; vector<int>vec; while(n) vec.push_back(n%2),n/=2; long long res=0,last=0; for(int i=vec.size()-1;i>=0;i--) { int x=vec[i]; if(x) { res+=f[i][k-last]; last++; if(last>k) break; } if(!i&&last==k) res++; } return res; }
int main() { init(); cin>>n>>k; cout<<dp(n)<<endl; return 0; }
|
加强一点,处理更多种进制:
1081. 度的数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include<bits/stdc++.h> using namespace std; const int N=35; int f[N][N],l,r,K,B;
int init() { for(int i=0;i<N;i++) for(int j=0;j<=i;j++) if(!j) f[i][j]=1; else f[i][j]=f[i-1][j]+f[i-1][j-1]; }
int dp(int n) { if(!n) return 0; vector<int>vec; while(n) vec.push_back(n%B),n/=B; int res=0,last=0; for(int i=vec.size()-1;i>=0;i--) { int x=vec[i]; if(x) { res+=f[i][K-last]; if(x>1) { res+=f[i][K-last-1]; break; } else { last++; if(last>K) break; } } if(i==0&&last==K) res++; } return res; } int main() { init(); cin>>l>>r>>K>>B; cout<<dp(r)-dp(l-1)<<endl; return 0; }
|
1082. 数字游戏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include<bits/stdc++.h> using namespace std; const int N=15; int f[N][N],l,r;
int init() { for(int j=0;j<=9;j++) f[1][j]=1; for(int i=2;i<N;i++) for(int j=0;j<=9;j++) for(int k=j;k<=9;k++) f[i][j]+=f[i-1][k]; }
int dp(int n) { if(!n) return 1; vector<int>vec; while(n) vec.push_back(n%10),n/=10; int res=0,last=0; for(int i=vec.size()-1;i>=0;i--) { int x=vec[i]; if(last>x) break; for(int j=last;j<vec[i];j++) res+=f[i+1][j]; last=x; if(!i) res++; } return res; } int main() { init(); while(cin>>l>>r) cout<<dp(r)-dp(l-1)<<endl; return 0; }
|
1083. Windy数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include<bits/stdc++.h> using namespace std; const int N=11; int f[N][N],l,r;
int init() { for(int j=0;j<=9;j++) f[1][j]=1; for(int i=2;i<N;i++) for(int j=0;j<=9;j++) for(int k=0;k<=9;k++) if(abs(j-k)>=2) f[i][j]+=f[i-1][k]; } int dp(int n) { if(!n) return 0; vector<int>vec; while(n) vec.push_back(n%10),n/=10; int res=0,last=-2; for(int i=vec.size()-1;i>=0;i--) { int x=vec[i]; for(int j=(i==vec.size()-1);j<x;j++) if(abs(j-last)>=2) res+=f[i+1][j]; if(abs(x-last)>=2) last=x; else break; if(!i) res++; } for(int i=1;i<vec.size();i++) for(int j=1;j<=9;j++) res+=f[i][j]; return res; }
int main() { init(); cin>>l>>r; cout<<dp(r)-dp(l-1)<<endl; return 0; }
|
1084. 数字游戏 II
f[i][j][k]
表示一共有i位,且最高位数字是j,且所有位数字和%P结果为k的数的个数,若要转移到f[i][j][k]
的状态,在i-1位对于每个x(x取值0~9)都应使第三维为(k-j)%P
状态转移方程:
$f[i][j][k]=\sum{k=0}^{P-1}\sum{x=0}^{9}f[i-1][x][(k-j)\%P]$
用last表示到当前为止,前面数位上的数字之和,对此,当前第i位数字为j,前面数字之和为last,那么
后i位(包括j这一位)数字之和sum与last的关系就是
(last+sum)%N==0
,那么sum%N==(-last)%N
,
所以res+=f[i+1][j][(-last%N)]
;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include<bits/stdc++.h> using namespace std; const int N=11; int f[N][N][110],l,r,P;
int mod(int u,int v) { return (u%v+v)%v; } int init() { memset(f,0,sizeof f); for(int i=0;i<=9;i++) f[1][i][i%P]++; for(int i=2;i<N;i++) for(int j=0;j<=9;j++) for(int k=0;k<P;k++) for(int x=0;x<=9;x++) f[i][j][k]+=f[i-1][x][mod(k-j,P)]; }
int dp(int n) { if(!n) return 1; vector<int>vec; while(n) vec.push_back(n%10),n/=10; int res=0,last=0; for(int i=vec.size()-1;i>=0;i--) { int x=vec[i]; for(int j=0;j<x;j++) res+=f[i+1][j][mod(-last,P)]; last+=x; if(!i&&last%P==0) res++; } return res; }
int main() { while(cin>>l>>r>>P) { init(); cout<<dp(r)-dp(l-1)<<endl; } return 0; }
|
1085. 不要62
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include<bits/stdc++.h> using namespace std; const int N=11; int f[N][N],l,r;
int init() { for(int j=0;j<=9;j++) if(j!=4) f[1][j]=1; for(int i=2;i<N;i++) for(int j=0;j<=9;j++) { if(j==4) continue; for(int k=0;k<=9;k++) { if(k==4||(j==6&&k==2)) continue; f[i][j]+=f[i-1][k]; } } } int dp(int n) { if(!n) return 1; vector<int>vec; while(n) vec.push_back(n%10),n/=10; int res=0,last=0; for(int i=vec.size()-1;i>=0;i--) { int x=vec[i]; for(int j=0;j<x;j++) { if(j==4||(j==2&&last==6)) continue; res+=f[i+1][j]; } if(x==4||(x==2&&last==6)) break; last=x; if(!i) res++; } return res; }
int main() { init(); while(cin>>l>>r,l||r) cout<<dp(r)-dp(l-1)<<endl; return 0; }
|
P8801 [蓝桥杯 2022 国 B] 最大数字
DFS,两个分支:用A处理/用B处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include<bits/stdc++.h> using namespace std; long long n,ans; int A,B; vector<int>vec;
void dp(int index,int a,int b,long long res) { if(index==-1) { ans=max(ans,res); return; } int x=vec[index]; if(x==9) dp(index-1,a,b,res*10+x); if(a&&x!=9) { int pl=min(a,9-x); dp(index-1,a-pl,b,res*10+x+pl); } if(b>=x+1&&x!=9) dp(index-1,a,b-(x+1),res*10+9); dp(index-1,a,b,res*10+x); }
int main() { cin>>n>>A>>B; while(n) vec.push_back(n%10),n/=10; dp(vec.size()-1,A,B,0); cout<<ans<<endl; return 0; }
|
P4317 花神的数论题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include<bits/stdc++.h> using namespace std; const int mod=10000007; long long n,ans=1,cnt; const int N=70; long long f[N][N],num[N]; vector<int>vec;
void init() { for(int i=0;i<N;i++) for(int j=0;j<=i;j++) if(!j) f[i][j]=1; else f[i][j]=f[i-1][j]+f[i-1][j-1]; }
long long ksm(long long base,long long power) { base%=mod; long long res=1; while(power) { if(power&1) res=res*base%mod; power>>=1; base=base*base%mod; } return res; }
long long dp(long long n) { while(n) vec.push_back(n&1),n>>=1; for(int i=vec.size()-1;i>=0;i--) { int x=vec[i]; if(x) { for(int j=1;j<=i;j++) num[cnt+j]+=f[i][j]; ++num[++cnt]; } } for(int i=0;i<vec.size();i++) ans*=ksm(i+1,num[i+1]),ans%=mod; return ans%mod; }
int main() { init(); cin>>n; cout<<dp(n)<<endl; return 0; }
|
P6218 [USACO06NOV] Round Numbers S
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include<bits/stdc++.h> using namespace std; int l,r; const int N=35; int f[N][N];
void init() { for(int i=0;i<N;i++) for(int j=0;j<=i;j++) if(!j) f[i][j]=1; else f[i][j]=f[i-1][j]+f[i-1][j-1]; }
int get(int x) { vector<int> num; while(x) num.push_back(x%2),x/=2; int n=num.size(); int res=0, last=0; for(int i=n-2;i>=0;i--) { if(num[i]) for(int j=max((n+1)/2-(last+1),0);j<=i;j++) res+=f[i][j]; else last++; if(!i&&last>=(n+1)/2) res++; } for(int i=1;i<n;i++) for(int j=(i+1)/2;j<i;j++) res+=f[i-1][j]; return res+1; } int main() { init(); cin>>l>>r; cout<<get(r)-get(l-1)<<endl; return 0; }
|
P2602 [ZJOI2010] 数字计数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include<bits/stdc++.h> using namespace std; typedef long long LL; LL f[20],cnta[20],cntb[20],ten[20],l,r;
void solve(long long x,long long *cnt) { long long num[20]={0}; int len=0; while(x) num[++len]=x%10,x=x/10; for(int i=len;i>=1;i--) { for(int j=0;j<=9;j++) cnt[j]+=f[i-1]*num[i]; for(int j=0;j<num[i];j++) cnt[j]+=ten[i-1]; long long num2=0; for(int j=i-1;j>=1;j--) num2=num2*10+num[j]; cnt[num[i]]+=num2+1; cnt[0]-=ten[i-1]; } }
int main() { cin>>l>>r; ten[0]=1; for(int i=1;i<=15;i++) { f[i]=f[i-1]*10+ten[i-1]; ten[i]=10*ten[i-1]; } solve(l-1,cnta); solve(r,cntb); for(int i=0;i<10;i++) cout<<cntb[i]-cnta[i]<<' '; return 0; }
|