1. 程式人生 > 實用技巧 >2020杭電多校第三場 1006- X Number 數位dp

2020杭電多校第三場 1006- X Number 數位dp

1006- X Number

題意

給你兩個整數 \(l,r\) 和一個數碼 \(d\) ,問在 \([l,r]\) 範圍內有多少個數中數碼 \(d\) 出現的次數嚴格大於其他數碼出現的次數。

分析

如果直接數位\(dp\)需要存每個數字出現的次數,陣列是開不下的,但是可以利用對\(limit\)的理解來做:

\(limit\)表示當前列舉的位有限制,表示前幾位都和原數字\(a_i\)是相同的,所以當前這一位只能從\(0\)列舉到\(a_i\)

當沒有限制時,後面的數字就可以隨便取,這時我們就可以直接計算答案了,用\(cnt[i]\)記錄列舉到當前位每個數字\(i\)出現了多少次,\(pos\)表示還有多少個剩餘位置是空的,列舉數碼\(d\)

出現的次數\(num\),就可以大力\(dp\)了,狀態\(dp[i][j]\)表示考慮\(0\sim 9\)中前\(i\)且位,除了指定的那一位以外已經在剩餘的位置安放了\(j\)個數。轉移如下:

\[dp[i][j]=\sum_{k=0}^{min(j,num-cnt[i]-1)}dp[i-1][j-k]\times C_{pos-j+k}^{k} \]

這個轉移可以自己手推一推。

Code

#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<sstream>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=1e5+10;
const int inf=1e9;
int T,d;
ll l,r;
int cnt[11];
ll dp[11][22],C[22][22];
int a[22];
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?a[pos]:9;
    ll ans=0;
    for(int i=0;i<=up;i++){
        if(!lead||i!=0) cnt[i]++;
        ans+=dfs(pos-1,limit&&i==a[pos],lead&&i==0);
        if(!lead||i!=0) cnt[i]--;
    }
    return ans;
}
ll solve(ll x){
    int len=0;
    while(x){
        a[len++]=x%10;
        x/=10;
    }
    return dfs(len-1,true,true);
}
int main(){
    //ios::sync_with_stdio(false);
    //freopen("in","r",stdin);
    C[0][0]=1;
    rep(i,1,20){
        C[i][0]=1;
        rep(j,1,i){
            C[i][j]=C[i-1][j]+C[i-1][j-1];
        }
    }
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld%d",&l,&r,&d);
        printf("%lld\n",solve(r)-solve(l-1));
    }
    return 0;
}