传送门:【HDU】5106 Bits Problem
题目分析:
dp[cur][cnt].num表示处理到第cur位,之后还需要cnt个1时,之后可以构成多少的方案数。
dp[cur][cnt].sum表示处理到第cur位,之后还需要cnt个1时,之后可以构成的数的数和。
那么有了num和sum后cur+1就可以利用cur的信息推出。
PS:比赛的时候犯了一个错误,没有好好利用信息,cnt一开始记录的是之前有多少个1,这样每次dp时我都需要初始化dp数组,且每次都是重新求,导致超时。之后发现完全可以倒过来表示还需要多少个1,那么大家一视同仁了,于是dp只要在一开始初始化一次即可,之后每次dp都可以利用之前所有case的信息,成功AC。
代码如下:
#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 ) const int MAXN = 1005 ; const int mod = 1e9 + 7 ; struct Node { int num , sum ; Node () {} Node ( int num , int sum ) : num ( num ) , sum ( sum ) {} } dp[MAXN][MAXN] ; char buf[MAXN] ; int digit[MAXN] ; int pow[MAXN] ; int n ; Node dfs ( int cur , int limit , int cnt , int x ) { if ( cnt < 0 ) return Node ( 0 , 0 ) ; if ( cur == -1 ) { if ( !cnt ) return Node ( 1 , 0 ) ; else return Node ( 0 , 0 ) ; } if ( !limit && ~dp[cur][cnt].sum ) return dp[cur][cnt] ; Node ans = Node ( 0 , 0 ) ; int n = limit ? digit[cur] : 1 ; For ( i , 0 , n ) { Node tmp = dfs ( cur - 1 , limit && i == n , cnt - i , i ) ; ans.num = ( ans.num + tmp.num ) % mod ; ans.sum = ( ans.sum + tmp.sum + ( LL ) tmp.num * i * pow[cur] ) % mod ; } if ( !limit ) dp[cur][cnt] = ans ; return ans ; } void solve () { int n1 = strlen ( buf ) ; rep ( i , 0 , n1 ) digit[n1 - i - 1] = buf[i] - '0' ; rep ( i , 0 , n1 ) { if ( digit[i] ) { digit[i] = 0 ; break ; } else digit[i] = 1 ; } Node ans = dfs ( n1 - 1 , 1 , n , 0 ) ; printf ( "%d\n" , ans.sum ) ; } int main () { pow[0] = 1 ; rep ( i , 1 , MAXN ) pow[i] = pow[i - 1] * 2 % mod ; clr ( dp , -1 ) ; while ( ~scanf ( "%d%s" , &n , buf ) ) solve () ; return 0 ; }