题目
点这里看题目。
分析
看见大家都写数位 DP ,可是我就是不会数位 DP 。
为了方便描述,不妨设 (m) 为进制,在这个问题中 (m=10) 。
类似于数位 DP ,我们可以限制当前的数在前 (i) 位与 (X) 相同,且在第 (i+1) 位比 (X) 小,那么在更低的位就没有限制了,我们可以方便地统计。
此时的问题就相当于是,我们仍然选 (n) 个数码,但在确定的前缀中,对于数码 (k) ,它至少会被选择 (l_k) 次,并且这 (l_k) 个的位置已经确定的。
那么不难想到如下 DP :
设 (g_{i,j}) 表示考虑了前 (i) 个非零数码后,已有的数字的总长度为 (j) 的方案数。
设 (f_{i,j}) 表示考虑了前 (i) 个非零数码后,已有的数字的总长度为 (j) 的所有 (f(X)) 的和。
另设 (V_k) 表示长度为 (k) 的全是 1 的数的值,即 (frac{m^k-1}{m-1}) 。
假设现在未被确定的有 (L) 个数码,转移也比较简单:
最后我们不需要考虑 0 的贡献,位置也不重要,所以:
现在我们就得到了 (O(m^2n^3)) 的算法。
考虑优化这个过程。由于 (f,g) 的转移比较简单,我们考虑推导生成函数。
设如下几个生成函数:
根据 (G) 的转移有:
根据 (F) 的转移有:
令 (p_i=m^{l_i}e^{mx},q_i=ie^{(i-1)x}(m^{l_i}V(x)+e^xV_{l_i})) ,则有:
考虑答案:
看到特殊的情况,即带入 (m=10) :
考虑计算这个式子,可以发现唯一的问题就在于 (e^{(90-9k)x}V(x)) 比较复杂。不过,由于 (k) 的值很少,我们可以直接枚举 (k) ,预处理 (e^{(90-9k)x}V(x)) 的结果。这样我们就可以 (O(m)) 计算一次 (ans) 。
这里 (V(x)) 可以得到一个闭形式: (V(x)=frac{1}{m-1}(e^{mx}-e^x)=frac{1}{m-1}e^x(e^{(m-1)x}-1)) ,那么可以得到闭形式为 (frac{1}{m-1}e^{(m(m-1-k)+k)x}e^x(e^{(m-1)x}-1)) ,这个东西就可以 (O(1)) 计算了。
时间复杂度是 (O(mn^2)) ,如果你喜欢,也可以变成 (O(mnlog_2n)) ,甚至 (O(m^2n)) 。
小结:
数学 无脑直接推 练习题,还是 Tiw 的做法更简单。
代码
点我查看代码
#include <cstdio>
#include <cstring>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
const int mod = 1e9 + 7;
const int MAXN = 700 + 5, MAXK = 10;
template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
}
template<typename _T>
_T MAX( const _T a, const _T b )
{
return a > b ? a : b;
}
int low[MAXK];
int fac[MAXN], ifac[MAXN];
int V[MAXN], pw[MAXN], spw[MAXN];
int X[MAXN];
char str[MAXN];
int N, ans = 0, inv9;
inline int Qkpow( int, int );
inline int Mul( int x, int v ) { return 1ll * x * v % mod; }
inline int Inv( const int a ) { return Qkpow( a, mod - 2 ); }
inline int Sub( int x, int v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }
int Qkpow( int base, int indx )
{
int ret = 1;
while( indx )
{
if( indx & 1 ) ret = Mul( ret, base );
base = Mul( base, base ), indx >>= 1;
}
return ret;
}
void Init()
{
spw[0] = pw[0] = 1, V[0] = 0;
rep( i, 1, N ) pw[i] = Mul( pw[i - 1], 10 ), V[i] = Add( V[i - 1], pw[i - 1] );
inv9 = Inv( 9 );
fac[0] = 1; rep( i, 1, N ) fac[i] = Mul( fac[i - 1], i );
ifac[N] = Inv( fac[N] ); per( i, N - 1, 0 ) ifac[i] = Mul( ifac[i + 1], i + 1 );
}
int Calc( const int L )
{
int ret = 0, prod = 1;
per( k, 9, 1 )
{
int coe = Mul( inv9, Sub( Mul( Qkpow( 100 - 9 * k, L ), ifac[L] ), Mul( Qkpow( 91 - 9 * k, L ), ifac[L] ) ) );
ret = Add( ret, Mul( Mul( prod, k ), Add( Mul( coe, pw[low[k]] ), Mul( V[low[k]], Mul( Qkpow( 91 - 9 * k, L ), ifac[L] ) ) ) ) );
prod = Mul( prod, pw[low[k]] );
}
return Mul( ret, fac[L] );
}
int main()
{
scanf( "%s", str + 1 ), N = strlen( str + 1 );
rep( i, 1, N ) X[N - i + 1] = str[i] - '0';
Init();
per( i, N, 1 )
{
rep( j, 0, X[i] - 1 )
{
low[j] ++;
ans = Add( ans, Calc( i - 1 ) );
low[j] --;
}
low[X[i]] ++;
}
ans = Add( ans, Calc( 0 ) );
write( ans ), putchar( '
' );
return 0;
}