统计d(1~9)在数字0~n里面出现的个数(数位dp)

规律:
首先枚举个位,十位,百位…,然后对于每一位,作为数字d的时候,包含的数字有多少个,分为这个数的左右2端。
比如12345,要求d=4有多少个,对于十位,这一位取4,分成12300和45来看,对于12300,十位取4,其左边是有123种情况,右边有10种,所以总数就是1230,然后对于45,取4的情况有6种。然后对于百位也同样道理。
code(以7为例)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e5+10;
#define IOS ios::sync_with_stdio(0)
#define ull unsigned ll
#define uint unsigned
#define pai pair<int,int>
#define pal pair<ll,ll>
#define IT iterator
#define pb push_back
#define fi first
#define se second
#define For(i,j,k) for (int i=(int)(j);i<=(int)(k);++i)
#define Rep(i,j,k) for (int i=(int)(j);i>=(int)(k);--i)
#define endl '\n'
#define ll long long
const ll mod = 1e9+7;
char str[man];
int fac[man],s[man];
 
signed main() {
    #ifndef ONLINE_JUDGE
        //freopen("in.txt", "r", stdin);
        //freopen("out.txt","w",stdout);
    #endif
    int t;
    scanf("%d",&t);
    fac[0] = 1;
    For(i,1,man-1)fac[i] = 1ll * fac[i-1] * 10 % mod;//处理10^i
    while(t--){
        scanf("%s",str+1);
        int n = strlen(str+1);
        For(i,1,n)s[i] = (10ll * s[i-1] + (str[i]-'0'))%mod;//计算前缀对mod取模
        ll ans = 0,tp = 0;
        Rep(i,n,1){
            ans = (ans + 1ll * s[i-1] * fac[n-i]% mod )%mod;//计算左右总的个数
            if(str[i]=='7')ans = (ans + tp + 1)%mod;//对于后一部分的计算
            else if(str[i]>'7')ans = (ans + fac[n-i])%mod;
            tp = (tp + 1ll*(str[i]-'0')*fac[n-i])%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}

数位dp
维护为d(0~9)的个数和总的个数,当为7的时候,直接把之前总的个数加到d的个数上。记忆化一下
以7位例
落谷P2602

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e5+10;
#define IOS ios::sync_with_stdio(0)
#define ull unsigned ll
#define uint unsigned
#define pai pair<int,int>
#define pal pair<ll,ll>
#define IT iterator
#define pb push_back
#define fi first
#define se second
#define For(i,j,k) for (int i=(int)(j);i<=(int)(k);++i)
#define Rep(i,j,k) for (int i=(int)(j);i>=(int)(k);--i)
#define endl '\n'
#define ll long long
const ll mod = 1e9+7;
int str[man];
int n;
pal dp[man];

template<typename t1,typename t2>
const pair<t1,t2> operator + (const pair<t1,t2> &a,const pair<t1,t2> &b){
	return {a.first + b.first,a.second +b.second};
}
int d;
pal dfs(int pos,bool limit,bool lead){
	if(pos==0)return {0,1};
	pal &x = dp[pos];
	if(!limit&&!lead&&x.first!=-1)return x;
	int up = limit ? str[pos] : 9;
	pal ans = {0,0};
	for(int i = 0;i <= up;++i){
		pal tp = dfs(pos-1,limit&&i==up,lead&&i==0);
		ans = ans + tp;
		if(lead&&i==0)continue;//判断前导0,如果有前导0并且当前为0,不统计当前位的答案。
		//如果有前导0,但i不等于0,该答案还是要统计,无前导0,i等于0,依旧统计答案。
		if(i==d)ans.first += tp.second;
	}
	if(!limit&&x.first==-1&&!lead)x = ans;
	return ans;
}

ll slove(ll x){
	int pos = 0;
	while(x){
		str[++pos] = x % 10;
		x /= 10;
	}
	return dfs(pos,1,1).first;
}

int main() {
	#ifndef ONLINE_JUDGE
		//freopen("in.txt", "r", stdin);
		//freopen("out.txt","w",stdout);
	#endif
	int t;
	for(int i = 0;i < man;++i)dp[i] = {-1,0};
	ll a,b;
	cin >> a >> b;
	for(int i = 0;i <= 9;++i){
		d = i;
		printf("%lld ",slove(b)-slove(a-1));
	}printf("\n");
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章