1. 程式人生 > 實用技巧 >2020杭電多校(三) X Number(數位dp)

2020杭電多校(三) X Number(數位dp)

這題如果普通數位dp,狀態不好表示,但是我們發現,一旦當dp過程中,脫離了被最高項束縛的狀態後,後面的數字就可以隨便填

因此我們直接用組合數dp計算,首先維護前面出現的數字的個數,之後列舉答案d出現的合法狀態的次數

設計dp[][],表示前i位,不包括d,在剩餘的個數中已經存了j的答案。因此我們可以列舉i後再列舉k嗎,計算這個位選k次的答案。

#include<bits/stdc++.h>
#define rep(i,x,n) for(int i=x;i<=n;i++)
using namespace std;
typedef long long ll;
typedef pair
<int,ll> pll; const int N=5e4+10; int c[1000][1000]; int s[N]; int d; int len; ll dp[25][25]; int cnt[N]; ll l,r; ll dfs(int pos,int limit,int lead){ if(pos==-1){ int mx=0,num=0; rep(i,0,9) if(cnt[i]>cnt[mx]) mx=i; rep(i,0,9) if(cnt[i]==cnt[mx]) num++; return mx==d&&num==1
; } if(!limit&&!lead){ ll ans=0; int mx=cnt[d]; rep(i,0,9) if(i!=d) mx=max(mx,cnt[i]+1); for(int num=mx;num<=cnt[d]+pos+1;num++){ memset(dp,0,sizeof(dp)); dp[0][0]=1; rep(i,1,10){ if(i==d+1){
for(int j=0;j<=20;j++) dp[i][j]=dp[i-1][j]; continue; } for(int j=0;j<=cnt[d]+pos+1-num;j++){ for(int k=0;k<=num-cnt[i-1]-1&&j-k>=0;k++){ dp[i][j]+=dp[i-1][j-k]*c[pos+1-j+k][k]; } } } ans+=dp[10][cnt[d]+pos+1-num]; } return ans; } int up=limit?s[pos]:9; ll ans=0; for(int i=0;i<=up;i++){ if(!lead||i!=0) cnt[i]++; ans+=dfs(pos-1,limit&&i==s[pos],lead&&i==0); if(!lead||i!=0) cnt[i]--; } return ans; } void init(){ int i,j; c[0][0]=1; for(i=1;i<=20;i++){ c[i][0]=1; for(j=1;j<=i;j++){ c[i][j]=c[i-1][j]+c[i-1][j-1]; } } } ll solve(ll x){ int len=0; if(x==0){ s[0]=0; len=1; } while(x){ s[len++]=x%10; x/=10; } return dfs(len-1,1,1); } int main(){ ios::sync_with_stdio(false); init(); int t; cin>>t; while(t--){ cin>>l>>r>>d; cout<<solve(r)-solve(l-1)<<endl; } }
View Code