学习了oiwiki的习题和数位dp,记录打卡一下。
都是比较经典的数位dp,感觉收益很大。
经典的数位dp。
dp[20][200][200],开三维dp,第一维表示位数的遍历,第二维表示当前所有数字的位数之和,第三位表示当前数字的模数。通过暴力去枚举所有有可能的和(9*len)。然后一起进行数位dp,从而求解出最终得数。
// #pragma GCC optimize(3) //O2优化开启
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int mod=1e9+7;
const int MX=0x3f3f3f3f3f3f3f3f;
// static char buf[100000],*pa=buf,*pd=buf;
// #define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
// inline int read()
// {
// register int x(0);register char c(gc);
// while(c<'0'||c>'9')c=gc;
// while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
// return x;
// }
int l,r;
int dp[20][200][200];
int a[50];
int query(int x){
if(x==0)return 0;
int len=0;
while(x)a[++len]=x%10,x/=10;
auto dfs=[&](auto self,int id,int f,int sum,int p,int sux)->int{
if(id==0){
return sum==p&&(sux==0);
}
if(f==0&&dp[id][sum][sux]!=-1)return dp[id][sum][sux];
if(sum>p)return 0;
int res=9;
if(f)res=a[id];
int cnt=0;
for(int i=0;i<=res;i++){
cnt+=self(self,id-1,f&(i==res),sum+i,p,(sux*10ll+i)%p);
}
return dp[id][sum][sux]=cnt;
};
int ans=0;
for(int i=1;i<=9*len;i++){
memset(dp,-1,sizeof dp);
ans+=dfs(dfs,len,1,0,i,0);
}
// cout<<ans<<"+++\n";
return ans;
}
void icealsoheat(){
cin>>l>>r;
l--;
cout<<query(r)-query(l);
}
signed main(){
ios::sync_with_stdio(false); //int128不能用快读!!!!!!
cin.tie();
cout.tie();
int _yq;
_yq=1;
// cin>>_yq;
while(_yq--){
icealsoheat();
}
}
dp[1005][10][10][2]
我们需要记录当前位数的位置,的上一位以及上上位置的数据值,以及当前是否已经满足了对称的条件从而分别作为数位dp的四维,来进行数位dp。
// #pragma GCC optimize(3) //O2优化开启
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int mod=1e9+7;
const int MX=0x3f3f3f3f3f3f3f3f;
// static char buf[100000],*pa=buf,*pd=buf;
// #define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
// inline int read()
// {
// register int x(0);register char c(gc);
// while(c<'0'||c>'9')c=gc;
// while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
// return x;
// }
string l,r;
int dp[1005][11][11][3];
int a[10005];
int len;
int ui;
int query(string &x){
// int len=0;
len=0;
for(int i=0;i<x.size();i++){
a[++len]=x[i]-'0';
}
auto dfs=[&](auto self,int id,int f,int f0,int le,int lle,int p)->int{
if(id>len){
return p;
}
if(le!=-1&&!f&&dp[id][le][lle][p]!=-1)return dp[id][le][lle][p];
int res=9;
if(f)res=a[id];
int cnt=0;
for(int i=0;i<=res;i++){
int yu=-1;
if(f0|i)yu=i;
cnt=(cnt+self(self,id+1,f&&(i==res),f0||(i!=0),yu,le,p||((i==le)&&f0)||((i==lle)&&f0)))%mod;
}
if(lle!=-1&&f0){
dp[id][le][lle][p]=cnt;
}
return cnt;
};
memset(dp,-1,sizeof dp);
int ans=dfs(dfs,1,1,0,-1,-1,0);
return ans;
}
void icealsoheat(){
cin>>l>>r;
int an=0;
for(int i=0;i<l.size();i++){
if(i>0&&l[i]==l[i-1]){
an=1;
}
if(i>1&&l[i]==l[i-2])an=1;
}
cout<<((query(r)-query(l)+an)%mod+mod)%mod;
}
signed main(){
ios::sync_with_stdio(false); //int128不能用快读!!!!!!
cin.tie();
cout.tie();
int _yq;
_yq=1;
// cin>>_yq;
while(_yq--){
icealsoheat();
}
}
我觉得很有代表性的一道题。
最开始想过用vector和map去维护1到9的数位dp,但最后以失败告终。看了题解才发现,我们只需要处理出1到9的最小公倍数lcm就行了。
三维dp代表了位数,当前数取模2520的值,以及lcm。
此时位dp[18][2520][2020],依旧会爆内存,但是我们可以发现,lcm一共有的数字不超过50个,我们需要提前预处理一下,可以把第三维优化成50。
与此同时,我们也不需要每一次都清空。我之前的数位dp都是正序遍历,并且每一次把dp值手动清空。但是这道题告诉我数位dp还是逆序遍历的好,逆序遍历在这道题中是可以不用每次都清空dp的值的,能大大减少时间复杂度(不然会t)。
#pragma GCC optimize(3) //O2优化开启
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,vector<int>> PII;
const int mod=2520;
// const int MX=0x3f3f3f3f3f3f3f3f;
// static char buf[100000],*pa=buf,*pd=buf;
// #define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
// inline int read()
// {
// register int x(0);register char c(gc);
// while(c<'0'||c>'9')c=gc;
// while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
// return x;
// }
string l,r;
int dp[20][2525][50];
int hh[2525];
int a[20];
void yu(){
int idx=0;
for(int i=1;i<=2520;i++){
if(2520%i==0){
hh[i]=++idx;
}
}
}
int LCM(int a,int b){
return a*b/__gcd(a,b);
}
int query(string &x){
int len=0;
for(int i=x.size()-1;i>=0;i--){
a[++len]=x[i]-'0';
}
// memset(dp,-1,sizeof dp);
auto dfs=[&](auto self,int id,int f,int sum,int lcm)->int{
if(!id)return sum%lcm==0;
if(dp[id][sum][hh[lcm]]!=-1&&f==0)return dp[id][sum][hh[lcm]];
int res=9;
if(f)res=a[id];
int ans=0;
for(int i=0;i<=res;i++){
int u=i?LCM(lcm,i):lcm;
ans+=self(self,id-1,f&&(i==res),(sum*10+i)%mod,u);
}
if(f==0)dp[id][sum][hh[lcm]]=ans;
return ans;
};
int ans= dfs(dfs,len,1,0,1);
// cout<<ans<<"+++\n";
return ans;
}
void icealsoheat(){
cin>>l>>r;
int an=0;
vector<int>p(10,0);
vector<int>ff(10,0);
for(int i=0;i<l.size();i++){
int x=l[i]-'0';
ff[x]=1;
for(int j=1;j<=9;j++){
p[j]=(p[j]*10+x)%j;
}
}
an=1;
for(int i=1;i<=9;i++){
if(p[i]&&ff[i]){
an=0;
break;
}
}
// cout<<(query(r)-query(l)+an)<<"\n";
printf("%lld\n",(query(r)-query(l)+an));
// cin>>l;
// query(l);
}
signed main(){
ios::sync_with_stdio(false); //int128不能用快读!!!!!!
cin.tie();
cout.tie();
int _yq;
_yq=1;
cin>>_yq;
memset(dp,-1,sizeof dp);
yu();
while(_yq--){
icealsoheat();
}
}
和之前的题很像,直接上代码
#pragma GCC optimize(3) //O2优化开启
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int mod=1e9+7;
const int MX=0x3f3f3f3f3f3f3f3f;
// static char buf[100000],*pa=buf,*pd=buf;
// #define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
// inline int read()
// {
// register int x(0);register char c(gc);
// while(c<'0'||c>'9')c=gc;
// while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
// return x;
// }
int m,d;
string L,R;
int dp[2005][2005];
void icealsoheat(){
cin>>m>>d;
cin>>L>>R;
L=' '+L;
R=' '+R;
int cnt=0;
auto dfs=[&](auto self,int id,int f,string &s,int sum)->int{
if(id>=s.size()){
return sum==0;
}
if(f==0&&dp[id][sum]!=-1)return dp[id][sum];
int res=9;
if(f)res=s[id]-'0';
int bns=0;
for(int i=0;i<=res;i++){
if(i==d&&(id&1))continue;
if(i!=d&&!(id&1))continue;
int sux=(sum*10+i)%m;
bns=(bns+self(self,id+1,f&(i==res),s,sux))%mod;
}
return dp[id][sum]=bns;
};
int ans=0;
memset(dp,-1,sizeof dp);
ans=dfs(dfs,1,1,R,0);
// cout<<ans<<"++++\n";
memset(dp,-1,sizeof dp);
ans=(ans-dfs(dfs,1,1,L,0))%mod+mod;
ans%=mod;
// cout<<ans;
bool ff=0;
int summ=0;
for(int i=1;i<L.size();i++){
summ*=10;
summ+=L[i]-'0';
summ%=m;
if(L[i]-'0'!=d&&!(i&1)){
ff=1;
}
else if((i&1)&&L[i]-'0'==d)ff=1;
}
if(!ff&&summ==0)ans++;
ans%=mod;
cout<<ans;
}
signed main(){
ios::sync_with_stdio(false); //int128不能用快读!!!!!!
cin.tie();
cout.tie();
int _yq;
_yq=1;
// cin>>_yq;
while(_yq--){
icealsoheat();
}
}