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;
}
posted @ 2020-07-29 12:53  xyq0220  阅读(398)  评论(0编辑  收藏  举报