数位dp

数位dp专用于大数据针对数位数码问题

例题1
题意:求出 \([l,r]\) 之间每个数码出现过多少次,\(1 \le l,r \le 10^{12}\)
思路:由于数据很大,考虑数位dp,因为全i位数码出现的次数相同,可以通过递归计算
\(f[i] = f[i-1] * 10 + 10^{i-1}\)
f[i]表示全i位数码出现的次数
所以算数码个数有两个情况
情况1 数码s在高位,有 \(10^{i-1}\)
情况2 数码s在低位,有 \(f[i-1]*10\)
接下来计算 \([1,p]\) 之间的次数
从高位向低位计算,避免算低位的时候多算
步骤1 计算高位取 \(0,1...,a[i]\) 的时候低位有多少个
步骤2 计算高位取 \(0,1,..,a[i]\) 的时候高位有多少个
步骤3 删掉高位取 \(a[i]\) 的时候,大于原数的次数
步骤4 去除前导0

#include <bits/stdc++.h>
using namespace std;
long long l,r,f[20],mi[20],ans1[20],ans2[20];
int a[20];
void solve(long long n,long long *ans){
	int len=0;
	long long tmp=n;
	while(tmp){
		a[++len]=tmp%10;
		tmp/=10;
	}
	tmp=n;
	for(int i=len;i>=1;i--){
		for(int j=0;j<10;j++){
			ans[j] += f[i-1]*a[i];
		}
		for(int j=0;j<a[i];j++){
			ans[j] += mi[i-1];
		}
		tmp -= mi[i-1]*a[i];
		ans[a[i]] += tmp+1;
		ans[0]-=mi[i-1];
	}
}
int main(){
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>l>>r;
	mi[0]=1ll;
	for(int i=1;i<=13;i++){
		f[i]=f[i-1]*10ll+mi[i-1];
		mi[i]=mi[i-1]*10ll;
	}
	solve(r,ans1),solve(l-1,ans2);
	for(int i=0;i<10;i++)cout<<(ans1[i]-ans2[i])<<" ";
	
	return 0;
}

例题2
题意:求 \([l,r]\) 区间中满足相邻数位差值不小于2(前导零除外)的个数
思路:本题没有任何性质,并且数据范围大至 \(10^{18}\) 只能进行数位dp, 定义两个维度,一个长度,一个数码
定义\(f[i][j]\) 表示长度为i,第i位数字是j
按照例题1先计算全部满足的数量
\(f[i][j] = \sum_{k=0}^9([|j-k| \ge 2]*(f[i-1][k]))\)
接下来计算 \([0,p]\) 区间满足的个数
情况1 最高位是0的时候,低位个数,有 \(\sum_{i=1}^{len-1} \sum_{j=1}^9 f[i][j]\)
情况2 最高位取 \(1,2,...,a[len]-1\)的时候,有 \(、sum_{i=1}^{a[i]-1} f[len][i]\)
情况3 最高位取 a[len] 的时候,需要不停逼近数,但是不超过,所以求个数需要从高位到低位
当枚举到第i位的时候,有 \(\sum_{i=0}^{a[i]-1} ([|i-a[i+1]| \ge 2]*(f[i][j]))\)
情况4 如果在枚举的过程中,原数自身存在不合法的就停止枚举,否则继续,如果直到第一位都合法的时候,自身也是合法的,则还有一个情况

#include <bits/stdc++.h>
using namespace std;
long long l,r,f[20][10];
int a[20];
long long solve(long long n){
	long long ans=0;
	if(n==0){
		return ans;
	}
	int len=0;
	long long tmp=n;
	while(tmp){
		a[++len]=tmp%10;
		tmp/=10;
	}
	for(int i=1;i<len;i++){
		for(int j=1;j<10;j++){
			ans+=f[i][j];
		}
	}
	for(int i=1;i<a[len];i++){
		ans+=f[len][i];
	}
	for(int i=len-1;i>0;i--){
		for(int j=0;j<a[i];j++){
			if(abs(j-a[i+1]) >= 2){
				ans+=f[i][j];
			}
		}
		if(abs(a[i]-a[i+1]) < 2)return ans;
	}
	ans++;
	return ans;
}
int main(){
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>l>>r;
	for(int i=0;i<10;i++)f[1][i]=1;
	for(int i=2;i<=18;i++){
		for(int j=0;j<10;j++){
			for(int k=0;k<10;k++){
				if(abs(k-j) >= 2){
					f[i][j]+=f[i-1][k];
				}
			}
		}
	}
	cout<<solve(r)-solve(l-1)<<"\n";
	
	return 0;
}
posted @ 2026-08-19 00:49  rdrd  阅读(12)  评论(0)    收藏  举报