#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;
ll dp[30][30][18*18*10];// dp[i][j][k]表示当前位置为i, 中心点为j, 前缀和为kint digit[30];/**
* @brief 数位dp
*
* @param p 当前位置
* @param center 中心点
* @param pre 前缀和, 如果最后结果为0, 说明这个数字是合法的
* @param limit 是否处于限制位
* @return ll
*/
ll Dfs(int p,int center,int pre,bool limit){if(p <0){return pre ==0;}if(pre <0)return0;if(!limit && dp[p][center][pre]!=-1)return dp[p][center][pre];int up =(limit ? digit[p]:9);
ll ans =0;for(int i=0;i<=up;i++){
ans +=Dfs(p -1, center, pre +(p - center)* i, limit && i == up);}if(!limit) dp[p][center][pre]= ans;return ans;}
ll solve(ll n){if(n <0)return0ll;int p =0;while(n >0){
digit[p++]= n %10;
n /=10;}
ll ans =0;for(int i=0;i<p;i++){
ans +=Dfs(p -1, i,0,true);}return ans - p +1;}intmain(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);int t;
cin >> t;memset(dp,-1,sizeof dp);while(t--){
ll l, r;
cin >> l >> r;
cout <<solve(r)-solve(l -1)<<'\n';}return0;}
7. hdu 3652 B-number
问数字中有
13
13
13且能够被
13
13
13整除的数字的个数,一定要注意
d
p
dp
dp方程的设法,我一开始想的是记录前一个数字,但是这样的状态转移是错误的,因为只考虑前一个数字是有后效性的,换而使用
0
,
1
,
2
0,1,2
0,1,2三个状态记录当前字符串的状况,这样转移才是对的
#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;int digit[35];int dp[35][3][13];// dp[i][j][k] 当前第i位, 前一个状态是j// j=0表示普通状态, j=1表示前一个是1, j=2表示前面有字符串是13, 模数为kintDfs(int p,int pre,int md,bool limit){if(p <0){return pre ==2&& md ==0;}if(!limit && dp[p][pre][md]!=-1)return dp[p][pre][md];int up =(limit ? digit[p]:9);int ans =0;for(int i=0;i<=up;i++){int tmp = pre;if(pre ==0&& i ==1) tmp =1;elseif(pre ==1){if(i ==3) tmp =2;elseif(i ==1) tmp =1;else{
tmp =0;}}
ans +=Dfs(p -1, tmp,(md *10+ i)%13, limit && i == up);}if(!limit) dp[p][pre][md]= ans;return ans;}intsolve(int n){int p =0;while(n >0){
digit[p++]= n %10;
n /=10;}returnDfs(p -1,0,false,true);}intmain(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);int n;memset(dp,-1,sizeof dp);while(cin >> n){
cout <<solve(n)<<'\n';}return0;}
8. hdu 4734 F(x)
让你找权重不大于
F
(
A
)
F(A)
F(A)的数字的个数
如果设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示当前为第
i
i
i位,权重为
j
j
j的方案数,应对单组数据还行,多测无法清空
考虑一种
d
p
dp
dp方式,能够让它的每一个状态能够应对所有数字而不是单一范围
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示当前位是
i
i
i,
j
=
s
u
m
a
−
s
u
m
j=sum_a-sum
j=suma−sum,
s
u
m
sum
sum表示当前前缀的权重,这样是一般化的
d
p
dp
dp方程,对于所有满足情况的方案数都是唯一的
#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;int digit[35];int dp[35][42000];int now_a;intDfs(int p,int now,bool limit){if(now_a < now)return0;if(p <0)return now <= now_a;assert(now_a - now <42000);if(!limit && dp[p][now_a - now]!=-1)return dp[p][now_a - now];int up =(limit ? digit[p]:9);int ans =0;for(int i=0;i<=up;i++){
ans +=Dfs(p -1, now +(1<< p)* i, limit && i == up);}if(!limit) dp[p][now_a - now]= ans;return ans;}intsolve(int n){int p =0;while(n >0){
digit[p++]= n %10;
n /=10;}returnDfs(p -1,0,true);}intmain(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);int t;
cin >> t;memset(dp,-1,sizeof dp);for(int kase=1;kase<=t;kase++){int a, b;
cin >> a >> b;
now_a =0;int k =1;while(a >0){
now_a += k *(a %10);
a /=10;
k <<=1;}
cout <<"Case #"<< kase <<": "<<solve(b)<<'\n';}return0;}
10. hdu 4507 吉哥系列故事――恨7不成妻
求平方和的数的个数,数位
d
p
dp
dp关键在于加上一位之后的影响,我们要记录之前枚举过了的位的和,以及平方和,再根据满足条件的数的个数来计算加上这一位对于答案的影响
#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;constint MOD =1e9+7;constint N =20;int digit[N];
ll pow10[N];structst{
ll cnt;// 满足条件的数的个数
ll sum;// 满足条件的数的和
ll sum2;// 满足条件的数的平方和st(){}st(ll cnt, ll sum, ll sum2){this->cnt = cnt;this->sum = sum;this->sum2 = sum2;}}dp[N][N][N];
st Dfs(int p,int md,int tot,bool limit){if(p <0){returnst(md !=0&& tot !=0,0,0);}if(!limit && dp[p][md][tot].cnt !=-1){return dp[p][md][tot];}int up =(limit ? digit[p]:9);
st ans(0,0,0);for(int i=0;i<=up;i++){if(i ==7)continue;auto tmp =Dfs(p -1,(i + md *10)%7,(i + tot)%7, i == up && limit);
ans.cnt += tmp.cnt;
ans.sum +=(tmp.sum +1ll* i * pow10[p]% MOD * tmp.cnt % MOD)% MOD;
ans.sum2 +=((tmp.sum2 +2ll* i * pow10[p]% MOD * tmp.sum % MOD)% MOD +1ll* i * tmp.cnt % MOD * pow10[p]% MOD * i % MOD * pow10[p]% MOD)% MOD;
ans.cnt %= MOD;
ans.sum %= MOD;
ans.sum2 %= MOD;}if(!limit) dp[p][md][tot]= ans;return ans;}
ll solve(ll n){int p =0;while(n >0){
digit[p++]= n %10;
n /=10;}returnDfs(p -1,0,0,true).sum2;}intmain(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);int t;
pow10[0]=1ll;for(int i=1;i<=18;i++){
pow10[i]= pow10[i -1]*10% MOD;}memset(dp,-1,sizeof dp);
cin >> t;while(t--){
ll l, r;
cin >> l >> r;
cout <<((solve(r)-solve(l -1))% MOD + MOD)% MOD <<'\n';}return0;}
然后我们设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示当前位为
i
i
i,前面的三进制数的状态为
j
j
j,因为
3
10
<
60000
3^{10}\lt60000
310<60000,所以完全开的下
#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;int digit[20];
ll dp[20][60000];boolck(int st){int p =0;while(st >0){if(st %3==1&& p %2==1){returnfalse;}if(st %3==2&& p %2==0){returnfalse;}
p +=1;
st /=3;}returntrue;}intmodify(int st,int pos){int p =0;int ans =1;int x = st;while(x >0){if(pos == p){break;}
x /=3;
p +=1;
ans *=3;}while(pos != p){
ans *=3;
p +=1;}if(x %3<=1) st += ans;else st -= ans;return st;}
ll Dfs(int p,int st,bool lead,bool limit){if(p <0){returnck(st);}if(!limit && dp[p][st]!=-1)return dp[p][st];int up =(limit ? digit[p]:9);
ll ans =0;for(int i=0;i<=up;i++){
ans +=Dfs(p -1,(lead && i ==0? st :modify(st, i)), lead && i ==0, limit && i == up);}if(!limit) dp[p][st]= ans;return ans;}
ll solve(__int128_t n){int p =0;while(n >0){
digit[p++]= n %10;
n /=10;}returnDfs(p -1,0,true,true);}intmain(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);int t;
cin >> t;memset(dp,-1,sizeof dp);while(t--){
__int128_t a =0, b =0;
string s;
cin >> s;int len = s.length();for(int i=0;i<len;i++){
a *=10;
a += s[i]-'0';}
cin >> s;
len = s.length();for(int i=0;i<len;i++){
b *=10;
b += s[i]-'0';}
cout <<solve(b)-solve(a -1)<<'\n';}return0;}