CFR 611 D. New Year and Ancient Prophecy ( DP, Hash, LCP )
題意:
給你一個大數。要求分割成若干個數字,滿足嚴格遞增,而且沒有領導 0。
問方案數對 1e9 + 7 取模。
制約:
1 ≤ N ≤ 5000
保證第一個字符不為 '0'。
解法:
dp[ i ][ j ]:最後一個數字是 S[ i : j ] 時的方案數。
想知道 dp[ i ][ j ] 是多少,顯然前面一個數字長度比 j - i 小的都可以拿到,這用 dp 上的前綴和就可以處理。而長度比 j - i 大的一定拿不到。剩下就是判斷等長的能不能拿到,LCP 判一下就知道哪個大了。
其實 LCP 根本就可以預處理。
複雜度:
O( N ** 2 * lg N )
#include <bits/stdc++.h> using namespace std; const int MOD = int( 1e9 + 7 ); const int BASE = 311; const int MAXN = 5000; int N; string S; int base[ MAXN + 1 ]; int ph[ MAXN + 1 ]; int dp[ MAXN ][ MAXN + 1 ]; // [ i, j ) int pdp[ MAXN + 1 ][ MAXN + 1 ]; // pdp[ i ][ j ] = sum( dp[ 0, i )[ j ] ) int gh( int x, int y ) { // [ x, y ) int res = ( 1LL * ph[ y ] - 1LL * ph[ x ] * base[ y - x ] ) % MOD; if( res < 0 ) res += MOD; return res; } int lcp( int x, int y, int h ) { // [ x, x + h ) : [ y, y + h ) int lb = 0, ub = h + 1; while( lb + 1 != ub ) { int mid = lb + ub >> 1; ( gh( x, x + mid ) == gh( y, y + mid ) ? lb : ub ) = mid; } return lb; } #define less my_less bool less( int a, int b, int c, int d ) { // [ a, b ) < [ c, d ) ? assert( b - a == d - c ); int h = lcp( a, c, b - a ); if( h == b - a ) return false; return S[ a + h ] < S[ c + h ]; } signed main() { ios::sync_with_stdio( 0 ); cin >> N; cin >> S; base[ 0 ] = 1; for( int i = 0; i < N; ++i ) { base[ i + 1 ] = 1LL * base[ i ] * BASE % MOD; ph[ i + 1 ] = ( 1LL * ph[ i ] * BASE + S[ i ] ) % MOD; } for( int i = 1; i <= N; ++i ) { dp[ 0 ][ i ] = 1; for( int j = 1; j < i; ++j ) { if( S[ j ] == '0' ) continue; int st; if( j < i - j ) st = 0; else st = less( j - ( i - j ), j, j, i ) ? j - ( i - j ) : j - ( i - j ) + 1; dp[ j ][ i ] = ( pdp[ j ][ j ] - pdp[ st ][ j ] ) % MOD; if( dp[ j ][ i ] < 0 ) dp[ j ][ i ] += MOD; } for( int j = 0; j < i; ++j ) { pdp[ j + 1 ][ i ] = ( pdp[ j ][ i ] + dp[ j ][ i ] ) % MOD; } } cout << pdp[ N ][ N ] << endl; return 0; }