P9640 [SNCPC2019] Digit Mode

rgw2010發表於2024-08-23

思路:

定義 \(F(l,r)\) 表示若已經確定了 \([1,l-1]\) 的數,且 \([l,r]\) 沒有限制的貢獻數。

\(n\) 的長度為 \(len\),考慮先求出 \([1,i](i \le len-1)\) 的貢獻(是沒有限制的),那麼每次列舉第 \(1\) 位數字 \(a_1 \in [1,9]\),算上 \(F(2,i)\) 的貢獻即可。

則該情況貢獻和為:

\[\sum_{i=1}^{len-1} \sum_{j=1}^9 F(2,i) \]

現在來考慮長度為 \(len\) 的數的貢獻的和,也考慮列舉第 \(i\) 位的數字 \(j\),那麼若想要使得 \([i+1,len]\) 沒有限制,則至少要滿足 \(i \in [0,n_i-1]\),為了使得後面不算重,故我們要每次算完 \([i+1,len]\) 的貢獻和後,需要令 \(a_i = n_i\)

則該情況貢獻和為:

\[\sum_{i=1}^{len} \sum_{j=[i=1]}^{n_i-1} F(i+1,len) \]

注意最後要單獨計算 \(n\) 本身的貢獻。

現在我們來考慮確定 \(a_{1,\cdots,l-1}\) 的情況下如何計算 \(F(l,r)\)

考慮列舉眾數最大值 \(i\) 和眾數的出現次數 \(j\),設 \(p_i\) 表示 \(i\) 的出現次數,那麼在後面 \([l,r]\)\(i\) 至少還需要出現 \(j-p_i\) 次;同時為了確保 \(i\) 是眾數,需要滿足 \([0,i-1]\) 的出現次數 \(\le j\),且 \([i+1,9]\) 的出現次數 \(\le j-1\);即現在我們要求將 \(r-l+1-(j-p_i)\) 個位置分配給除了 \(i\) 以外的 \(9\) 個數的且滿足限制的方案數。

設我們列舉的眾數為 \(k\),眾數出現次數為 \(mx\),使用動態規劃演算法,定義 \(f_i\) 表示可以分配 \(i\) 個位置的方案數,考慮列舉選的數 \(j\) 和分配給 \(j\) 的數量 \(k\),揹包形轉移:

\[f_i = \sum_{j=0}^9 [j \ne k] \sum_{k=0}^{\min(mx,p_j-[i>k])} \binom{i}{k} f_{i-k} \]

可以先預處理階乘和逆元來計算組合數。

注意要先列舉 \(j\)

完整程式碼:

#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
#define For(i,l,r) for(int i=l;i<=r;i++)
#define _For(i,l,r) for(int i=r;i>=l;i--)
using namespace std;
typedef long double lb;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=55,mod=1e9+7;
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
inline void write(ll x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
}
ll T,n,t,g,ans,sum;
ll a[N],h[N],p[N],f[N],fac[N],inv[N];
char s[N];
ll qpow(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1ll)
          ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1ll;
    }
    return ans;
}
void init(){
    fac[0]=fac[1]=1;
    For(i,2,N-1)
      fac[i]=(fac[i-1]*i)%mod;
    inv[N-1]=qpow(fac[N-1],mod-2);
    _For(i,0,N-2)
      inv[i]=inv[i+1]*(i+1)%mod;
}
ll C(ll n,ll m){
    if(m>n||m<0)
      return 0;
    if(!m||n==m)
      return 1;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
ll get(ll n,ll k,ll mx){
	memset(f,0,sizeof(f));
    f[0]=1;
	For(i,0,9){
		if(i==k)
		  continue;
        t=mx-(i>k)-p[i];
		if(t<0)
		  return 0;
        _For(j,1,n)
          For(k,1,min((int)t,j))
			f[j]=(f[j]+f[j-k]*C(j,k)%mod)%mod;
	}
	return f[n];
}
ll get(ll l,ll r){
    sum=0;
    For(i,0,9)
      p[i]=0;
    For(i,1,l-1)
      p[a[i]]++;
    For(i,1,9)
      For(j,p[i],p[i]+r-l+1)
        sum=(sum+get(r-l+1-j+p[i],i,j)*C(r-l+1,r-l+1-j+p[i])%mod*i%mod)%mod;
    return sum;
}
void solve(){
    ans=0;
    scanf("%s",s+1);
    n=strlen(s+1);
    For(i,1,n)
      h[i]=s[i]-'0';
    For(i,1,n-1){
        For(j,1,9){
            a[1]=j;
            ans=(ans+get(2,i))%mod;
        }
    }
    For(i,1,n){
        For(j,(i==1),h[i]-1){
            a[i]=j;
            ans=(ans+get(i+1,n));
        }
        a[i]=h[i];
    }
    write((ans+get(n+1,n))%mod);
    putchar('\n');
}
bool End;
int main(){
    init();
    T=read();
    while(T--)
      solve();
	//cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
	return 0;
}