[CF908G] New Year and Original Order
题目
点这里看题目。
分析
看见大家都写数位 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(mn\log_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( '\n' );
return 0;
}