1.基本模型

数位dp,即以数的每一位作为状态进行dp的算法。通常状态为 \(f_{i,0-9}\) 表示第 \(i\) 为取 \(0-9\) 时的dp值。通常时间复杂度为 \(log_{10}n\) ,十分优秀。

2.套路

  1. 求区间合法类的题,使用容斥思想思想求解,即 \([1,r]-[1,l-1]\)
  2. dp式子一般很简单,可以使用矩阵快速幂优化
  3. 前导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;
    }
}
posted on 2023-05-04 18:26  lizhous  阅读(19)  评论(0编辑  收藏  举报