X Number(数位 指数型母函数)

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)!} (lenadd)!xlenadd的系数就是其他数字的排列方案数,再乘上 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;
}

你可能感兴趣的:(数论/数学,例题)