1. 程式人生 > >(數位dp)E 詭異數字

(數位dp)E 詭異數字

牛客小白月賽8真的打的自閉了,感覺一點都不小白 T_T (肯定是我太菜了,沒錯就是這樣的)

題解說這是一個非常簡單的數位dp,沒接觸過,感覺挺難的(大概這就是菜吧)

然後看懂了大佬的程式碼,敲了一下再附上了我的理解

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=20020219;
const ll maxn=1e3+5;
ll lim[20],len[20];//lim分別記錄每個數的最大重複次數,沒有就置大 
ll a[100][100][100];//len將數存入陣列,每個位置也代表著上限 
//  pos   pre   num
// 記錄著位數為pos(包括前導0,0012也算4位數),前驅是pre,前驅重複數為num的滿足條件的個數
// pos位的每一個位置的範圍都是0-9,(就是代表完整的pos位數) 
ll le,ri,n; 
ll dfs(int pos,bool limit,int pre,int num){
	if(num>lim[pre])	return 0;//超過pre允許出現的最大次數,返回0 
	if(pos==0)	return 1;// 0位返回1 
	if(!limit && a[pos][pre][num]!=-1) return a[pos][pre][num];//前驅不是上限,而且被更新過直接用 
						//如果前驅是上限,則後面上限也受到限制,不是一個完整的pos位,故不可用 
	int up=limit?len[pos]:9; //如果前驅達到上限,則這個位置的上限為解決數的上限
	ll sum=0;
	for(int i=0;i<=up;++i) {
		sum=(sum+dfs(pos-1,limit&&i==len[pos],i,i==pre?num+1:1))%mod;
	}	
	return limit?sum:a[pos][pre][num]=sum;//前驅不是上限就更新,否則直接return 
}

ll solve(ll xx) {
	if(xx==-1)	return 0;
	ll cnt=0;
	while(xx){
		len[++cnt]=xx%10;//每一位放入陣列,cnt代表位數 
		xx/=10;
	}
	ll ans=0;
	for(int i=0;i<=len[cnt];++i){//最高位從0-len[cnt]列舉 
		ans=(ans+dfs(cnt-1,i==len[cnt],i,1))%mod;
	} 
	return ans%mod;
}

int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		fill(lim,lim+11,0xffffffff);
		memset(a,-1,sizeof(a));
		scanf("%lld%lld%lld",&le,&ri,&n);
		ll xx,max_num;
		for(ll i=0;i<n;++i){
			scanf("%lld%lld",&xx,&max_num);
			lim[xx]=min(lim[xx],max_num);//更新 xx最大能出現lim[xx]次		
		}
		cout<<(solve(ri)-solve(le-1)+mod)%mod<<endl;
	}
	return 0;
}