http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1006&cid=881
题意:
给 L , R , d L,R,d L,R,d,求 [ L , R ] [L,R] [L,R]中有多少个数,满足十进制位上 d d d最多(严格最多)
解析:
从前往后遍历十进制位, c [ i ] c[i] c[i]维护当前位置之前数字 i i i出现的次数。
当前位枚举 [ 0 , H ( 上 限 ) − 1 ] [0,H(上限)-1] [0,H(上限)−1],接下来的位就可以任意选择了。考虑这种情况下的方案数求解。
枚举接下来 d d d的次数,就可以得到其他数的上限,通过指数型母函数求解排列方案。
假设接下来 d d d选了 a d d add add次:
∏ i ∈ [ 0 , 9 ] i ≠ d ( 1 + x + . . . x l i m i l i m i ! ) \prod_{i\in[0,9]}^{i\not=d}(1+x+...\dfrac{x^{lim_i}}{lim_i!}) ∏i∈[0,9]i=d(1+x+...limi!xlimi)中 x l e n − a d d ( l e n − a d d ) ! \dfrac{x^{len-add}}{(len-add)!} (len−add)!xlen−add的系数就是其他数字的排列方案数,再乘上 C l e n a d d C_{len}^{add} Clenadd把 d d d算进去。
这个系数由于len比较小直接用long double即可。
有个比较麻烦的是首位0的判断,细节比较多,除了最后1位以外的0是不计入 c c c数组的,这个部分可以通过dfs转化为上面的部分。
代码:
/*
* Author : Jk_Chen
* Date : 2020-07-28-10.02.53
*/
#include
using namespace std;
#define LL long long
#define LD long double
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair
#define fi first
#define se second
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<"> "<<x<<" ";test(args...);}
const LL mod=1e9+7;
const int maxn=1e5+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
#define rd rd()
/*_________________________________________________________begin*/
int c[10];
int d;
bool check(){
int mx=-1;
rep(i,0,9){
mx=max(mx,c[i]);
}
if(c[d]!=mx)return 0;
rep(i,0,9){
if(i!=d && c[i]==mx)return 0;
}
return 1;
}
LD vec[20];
LD tmp[20];
LL fac[20];
LD invfac[20];
LL C(int n,int m){
return fac[n]/fac[m]/fac[n-m];
}
LL cal(int reslen){
if(reslen==0)return check();
LL ans=0;
rep(add_d,0,reslen){
int lim[10];
bool cant=0;
int sumlim=0;
rep(i,0,9){
if(i==d)continue;
lim[i]=c[d]+add_d-1-c[i];
sumlim+=lim[i];
if(lim[i]<0){
cant=0;break;
}
}
if(cant||sumlim+add_d<reslen)
continue;
mmm(vec,0);
vec[0]=1;
int up=reslen-add_d;
rep(i,0,9){
if(i==d)continue;
mmm(tmp,0);
rep(j,0,lim[i]){
rep(k,0,up){
if(j+k>up)break;
tmp[j+k]+=vec[k]*invfac[j];
}
}
rep(j,0,up)vec[j]=tmp[j];
}
LL part=(LL)round(vec[up]*(LD)fac[up]);
ans+=part*C(reslen,add_d);
}
return ans;
}
LL cal0(int reslen){
if(reslen==0)return check();
LL ans=0;
rep(i,1,9){
c[i]++;
ans+=cal(reslen-1);
c[i]--;
}
if(reslen==1)
c[0]++;
ans+=cal0(reslen-1);
return ans;
}
LL getAns(const char *x,int len,int p=1){
if(p>len){
return check();
}
LL ans=0;
rep(i,1,x[p]-1){
c[i]++;
ans+=cal(len-p);
c[i]--;
}
// 0...
if(x[p]!=0){
if(p==1){
if(len==1){
if(d==0)ans++;
}
else{
ans+=cal0(len-p);
mmm(c,0);
}
}
else{
c[0]++;
ans+=cal(len-p);
c[0]--;
}
}
c[x[p]]++;
ans+=getAns(x,len,p+1);
return ans;
}
LL Prin(LL l,LL r){
LL ans=0;
for(LL i=l;i<=r;i++){
mmm(c,0);
LL tmp=i;
while(tmp){
c[tmp%10]++;
tmp/=10;
}
if(check())ans++;//,test(i);
}
printf("real %lld\n",ans);
}
int main(){
invfac[0]=1;
fac[0]=1;
rep(i,1,18)
invfac[i]=invfac[i-1]/i,
fac[i]=fac[i-1]*i;
int t=rd;
while(t--){
char x[30],y[30];
int lx,ly;
#define TEST_
#ifdef TEST
LL l=rd,r=rd;
LL tmpl=l,tmpr=r;
d=rd;
lx=0,ly=0;
while(l){
x[++lx]=l%10;
l/=10;
}
while(r){
y[++ly]=r%10;
r/=10;
}
reverse(x+1,x+1+lx);
reverse(y+1,y+1+ly);
l=tmpl,r=tmpr;
Prin(l,r);
#else
scanf("%s%s%d",x+1,y+1,&d);
lx=strlen(x+1);
ly=strlen(y+1);
rep(i,1,lx)x[i]-='0';
rep(i,1,ly)y[i]-='0';
assert(!(lx>1&&x[1]==0));
assert(!(ly>1&&y[1]==0));
#endif // TEST
mmm(c,0);
LL Ans=getAns(y,ly);
mmm(c,0);
Ans-=getAns(x,lx);
mmm(c,0);
rep(i,1,lx){
c[x[i]]++;
}
if(check())Ans++;
printf("%lld\n",Ans);
}
return 0;
}