題目連結:http://codeforces.com/problemset/problem/914/C
題意:
對數字x進行一次操作,可以將數字x變為x在二進位制下1的個數。
顯然,一個正整數在進行了若干次操作後一定會變成1。
給定n,k(n用二進位制表示給出,n <= 2^1000)。
問你有多少不超過n的正整數,將它們變為1所需的操作次數恰好為k。
題解:
由於n <= 2^1000,所以任何不超過n的數在進行了一次操作後,一定不超過1000。
所以先統計出1000以內所有數變成1所需的操作次數:f[i] = f[cal_bit(i)] + 1
那麼最終答案 = ∑(恰好包含i個1,且不超過n的數字個數),其中f[i] == k-1。
所以接下來就要求恰好包含i個1,且不超過n的數字個數:
表示狀態:
dp[i][j][0/1]表示已經填了前i位數,用了j個1,是否與n匹配(0/1),此時的方案數。
(n的最高位為第1位)
找出答案:
恰好包含i個1,且不超過n的數字個數 = dp[n][i][0] + dp[n][i][1]
如何轉移:
對於dp[i][j][0]:
dp[i+1][j][0] += dp[i][j][0]
dp[i+1][j+1][0] += dp[i][j][0]
對於dp[i][j][1]:
如果n的第i+1位為1:
dp[i+1][j][0] += dp[i][j][1]
dp[i+1][j+1][1] += dp[i][j][1]
否則:
dp[i+1][j][1] += dp[i][j][1]
邊界條件:
dp[1][0][0] = dp[1][1][1] = 1
最後統計下答案就好。
其中k==0或1的情況要特判。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX_N 1005 5 #define MOD 1000000007 6 7 using namespace std; 8 9 int n,k; 10 int ans=0; 11 int a[MAX_N]; 12 int f[MAX_N]; 13 int dp[MAX_N][MAX_N][2]; 14 string s; 15 16 void read() 17 { 18 cin>>s>>k; 19 n=s.size(); 20 for(int i=0;i<n;i++) a[i+1]=s[i]-'0'; 21 } 22 23 int cal_bit(int x) 24 { 25 int cnt=0; 26 int lowbit=x&-x; 27 while(lowbit) 28 { 29 cnt++; 30 x^=lowbit; 31 lowbit=x&-x; 32 } 33 return cnt; 34 } 35 36 void cal_f() 37 { 38 f[1]=0; 39 for(int i=2;i<=1000;i++) 40 { 41 f[i]=f[cal_bit(i)]+1; 42 } 43 } 44 45 void cal_dp() 46 { 47 memset(dp,0,sizeof(dp)); 48 dp[1][0][0]=dp[1][1][1]=1; 49 for(int i=1;i<n;i++) 50 { 51 for(int j=0;j<=i;j++) 52 { 53 if(dp[i][j][0]) 54 { 55 dp[i+1][j][0]+=dp[i][j][0]; 56 dp[i+1][j+1][0]+=dp[i][j][0]; 57 dp[i+1][j][0]%=MOD; 58 dp[i+1][j+1][0]%=MOD; 59 } 60 if(dp[i][j][1]) 61 { 62 if(a[i+1]) 63 { 64 dp[i+1][j][0]+=dp[i][j][1]; 65 dp[i+1][j+1][1]+=dp[i][j][1]; 66 dp[i+1][j][0]%=MOD; 67 dp[i+1][j+1][1]%=MOD; 68 } 69 else 70 { 71 dp[i+1][j][1]+=dp[i][j][1]; 72 dp[i+1][j][1]%=MOD; 73 } 74 } 75 } 76 } 77 } 78 79 void cal_ans() 80 { 81 for(int i=1;i<=1000;i++) 82 { 83 if(f[i]==k-1) 84 { 85 ans+=dp[n][i][0]+dp[n][i][1]; 86 ans%=MOD; 87 } 88 } 89 } 90 91 void work() 92 { 93 if(k==0) 94 { 95 cout<<1<<endl; 96 return; 97 } 98 cal_f(); 99 cal_dp(); 100 cal_ans(); 101 if(k==1) cout<<ans-1<<endl; 102 else cout<<ans<<endl; 103 } 104 105 int main() 106 { 107 read(); 108 work(); 109 }