传送门:【HDU】4352 XHXJ's LIS
题目分析:dp[cur][s][cnt]表示处理到第i位(从高到底),二进制状态为s,离成为长度为k的最长递增子序列还需要cnt位数。
重要的是表示状态的s,s最大1023((1<<10)-1)。s的每一位分别表示对应位的数字是否用于构成上升子序列,1表示用于,0相反。且以这个数字作为结尾能构成的最长上升子序列的长度为包括这个点在内从s二进制的高位到这一位中1的个数(首先要满足这个数的这一位为1)。
然后以这一层的状态推下一层的状态时,就像二分搜索O(nlogn)构造LIS一样替换或者插入即可。
代码如下:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> //#include <cmath> using namespace std ; typedef long long LL ; #pragma comment ( linker , "/STACK:1024000000" ) #define rep( i , a , b ) for ( int i = ( a ) ; i < ( b ) ; ++ i ) #define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i ) #define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i ) #define rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] ) #define clr( a , x ) memset ( a , x , sizeof a ) LL dp[19][1024][11] ; LL L , R ; int k ; int digit[19] , n1 ; int search ( int x , int a[] , int l , int r ) { while ( l < r ) { int m = ( l + r ) >> 1 ; if ( a[m] >= x ) r = m ; else l = m + 1 ; } return a[l] ; } LL dfs ( int cur , int limit , int s , int cnt ) { if ( cur == -1 ) return cnt == 0 ; if ( !limit && ~dp[cur][s][cnt] ) return dp[cur][s][cnt] ; LL ans = 0 , n = limit ? digit[cur] : 9 ; //printf ( "n = %lld\n" , n ) ; int top = 0 , a[10] ; For ( i , 0 , 9 ) if ( s & ( 1 << i ) ) a[top ++] = i ; For ( i , 0 , n ) { if ( !top ) { if ( i ) ans += dfs ( cur - 1 , limit && i == n , s ^ ( 1 << i ) , cnt - 1 ) ; else ans += dfs ( cur - 1 , limit && i == n , s , cnt ) ; } else if ( i > a[top - 1] ) { if ( cnt ) ans += dfs ( cur - 1 , limit && i == n , s ^ ( 1 << i ) , cnt - 1 ) ; } else { int x = search ( i , a , 0 , top - 1 ) ; ans += dfs ( cur - 1 , limit && i == n , s ^ ( 1 << i ) ^ ( 1 << x ) , cnt ) ; } //printf ( "%d %lld\n" , cur , ans ) ; } return limit ? ans : dp[cur][s][cnt] = ans ; } LL solve ( LL n ) { n1 = 0 ; while ( n ) { digit[n1 ++] = n % 10 ; n /= 10 ; } LL ans = dfs ( n1 - 1 , 1 , 0 , k ) ; return ans ; } int main () { int T , cas = 0 ; clr ( dp , -1 ) ; scanf ( "%d" , &T ) ; while ( T -- ) { scanf ( "%I64d%I64d%d" , &L , &R , &k ) ; printf ( "Case #%d: %I64d\n" , ++ cas , solve ( R ) - solve ( L - 1 ) ) ; } return 0 ; }