1.基本模型
数位dp,即以数的每一位作为状态进行dp的算法。通常状态为 \(f_{i,0-9}\) 表示第 \(i\) 为取 \(0-9\) 时的dp值。通常时间复杂度为 \(log_{10}n\) ,十分优秀。
2.套路
- 求区间合法类的题,使用容斥思想思想求解,即 \([1,r]-[1,l-1]\)
- dp式子一般很简单,可以使用矩阵快速幂优化
- 前导0和值域限制需要单独dp可以再开一维 \(0/1\),表示当前 \(0\) 是否有是前导(即这一位是否有限制)。
3.例题
Windy数(套路1,3)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#define mod 1000000007
#define int long long
using namespace std;
int l,r,f[10][10];
int get(int r)
{
int sy[10],len=0,re=0;
while(r)
{
sy[++len]=r%10; //拆位
r/=10;
}
reverse(sy+1,sy+len+1);
memset(f,0,sizeof(f));
sy[0]=-3;
bool nowJS=true;
for(int i=1;i<=len;i++) //数位
{
for(int z=0;z<10;z++) //这一位
{
for(int j=0;j<=9;j++) //上一位
{
if(j>max(-1ll,z-2)&&j<min(10ll,z+2)) continue; //不在范围内
f[i][z]+=f[i-1][j]; //统计
}
if(sy[i]>z&&abs(sy[i-1]-z)>=2&&i!=1){f[i][z]+=nowJS;} //破除值域限制
else if(i==1&&sy[i]>z&&z!=0){f[i][z]+=nowJS;} //第一位特殊处理
if(i!=1&&z!=0){f[i][z]++;} //破除前导0
}
if(abs(sy[i]-sy[i-1])<2){nowJS=false;} //维护极限值是否合法,用于值域限制
}
for(int i=0;i<10;i++)
{
re+=f[len][i];
}
return re+nowJS; //记得
}
signed main()
{
cin>>l>>r;
cout<<max(get(r)-get(l-1)+(l==1),0ll);
}
Sam数(套路2)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#define mod 1000000007
#define int long long
using namespace std;
int n,ans;
struct mat
{
int mat[12][12];
}a,c;
mat mult(mat a,mat b)
{
mat c;
memset(c.mat,0,sizeof(c.mat));
for(int k=0;k<=9;k++)
{
for(int i=0;i<=9;i++)
{
for(int z=0;z<=9;z++)
{
c.mat[i][z]+=a.mat[i][k]*b.mat[k][z]%mod;
c.mat[i][z]%=mod;
}
}
}
return c;
}
mat mul(mat a,mat b)
{
mat c;
memset(c.mat,0,sizeof(c.mat));
for(int k=0;k<=9;k++)
{
for(int i=0;i<=9;i++)
{
for(int z=0;z<=9;z++)
{
c.mat[i][z]+=a.mat[i][k]*b.mat[k][z]%mod;
c.mat[i][z]%=mod;
}
}
}
return c;
}
mat operator *(mat a,mat b)
{
mat c;
memset(c.mat,0,sizeof(c.mat));
for(int k=0;k<=9;k++)
{
for(int i=0;i<=9;i++)
{
for(int z=0;z<=9;z++)
{
c.mat[i][z]+=a.mat[i][k]*b.mat[k][z]%mod;
c.mat[i][z]%=mod;
}
}
}
return c;
}
mat operator ^(mat a,int b)
{
//init
mat re;
memset(re.mat,0,sizeof(re.mat));
for(int i=0;i<=9;i++)
{
re.mat[i][i]=1;
}
//ksm
while(b)
{
if(b&1)
{
re=mul(re,a);
}
a=mul(a,a);
b>>=1;
}
return re;
}
signed main()
{
for(int i=1;i<=9;i++)
{
a.mat[1][i]=1;
}
cin>>n;
for(int z=0;z<=9;z++)
{
for(int j=max(z-2,0ll);j<=min(9ll,z+2);j++)
{
c.mat[z][j]=1;
}
}
a=mult(a,c^(n-1));
for(int i=0;i<=9;i++)
{
ans=(ans+a.mat[1][i])%1000000007;
}
if(n>1)
{
cout<<ans;
}
else
{
cout<<ans+1;
}
}
标签:sy,mat,int,笔记,&&,include,dp,数位
From: https://www.cnblogs.com/lizhous/p/17372172.html