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;
}