Codeforces 914C Travelling Salesman and Special Numbers:數位dp

Leohh發表於2018-02-23

題目連結: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 }

 

相關文章