codeforces 477D

題意:給定一個長度<=5000的二進制字符串S,以及一個初始爲0的n,有一下兩種操做:c++

        1. 輸出n(n>=1而且爲2進制形式輸出)ide

        2.n=n+1spa

       每次選擇一種操做。。code

       求:1.有多少方案能構成該字符串 (%(10^9+7))blog

             2.最少須要多少次能構成該字符串(%(10^9+7))字符串

思路:it

    對於第一問:event

      能容易想到O(n^3)的dpclass

      f[i][j] += f[k][i-1](其中[k,j-1]構成的數<=[i, j]組成的數,而且i,k位置爲'1')cli

      可是這樣時限上顯然不夠,由於比較大小太耗時間了。。

      因爲是01串,咱們能夠先預處理same[i][j]表示以i開頭和以j開頭的數字最多多少個相同的。。這樣判斷就能夠O(1)了

   對於第二問:

      仍是能夠用dp。。

      f[i][j]表示以j結尾,[i, j]爲最後一個數時最少分爲幾段。。思路跟上面差很少。。

      可是比較大小可能須要一點技巧。。

      基於最後結尾的數多一位,那麼add操做次數*2,那麼也就是說當最後的數很大是(add>=|S|),越小的結尾的數越優

      因此,對於最後結尾的數比較小的,直接算出來,由於答案不會很大。。

      不然的話,直接找結尾的數最小的就是最優解了。

      固然,也能夠基於上面的結論直接貪心。。

code:

 1 #include <bits/stdc++.h>
 2 #define M 1000000007
 3 #define Inf 0x3fffffff
 4 using namespace std;
 5 char s[5100];
 6 int same[5001][5001], f[5001][5001], c[5001][5001];
 7 
 8 inline void add(int& c, const int &a, const int& b){
 9      c = a + b;
10      if (c >= M) c -= M;
11 }
12 
13 int bigger(const int& x, const int& y, const int& k){
14     if (y <= 0) return 0;
15     int len = same[y][x];
16     if (len >= k) return 1;
17     return s[x+len] >= s[y+len]; 
18 }
19 
20 void solve(){
21      int n = strlen(s + 1);
22      for (int i = n; i >= 1; --i)
23           for (int j = i; j <= n; ++j)
24               if (s[i] == s[j]) same[i][j] = same[i+1][j+1] + 1;
25               else same[i][j] = 0;
26      for (int i = 1; i <= n; ++i)
27             f[1][i] = 1;
28      c[1][1] = f[1][1];
29      for (int i = 2; i <= n; ++i){
30           for (int j = i; j > 1; --j) if (s[j] == '1'){
31                 add(f[j][i], f[j][i], c[j-1][min(i-j, j-1)]);
32                 if (bigger(j, j-1-(i-j), i-j+1)){
33 //                     if (j == 11 && i == 17) printf("%d %d %d\n", j, j-1-(i-j), i-j+1);
34                      add(f[j][i], f[j][i], f[j-1-(i-j)][j-1]);
35                 }
36           }
37           for (int j = i; j >= 1; --j)
38                 add(c[i][i-j+1], c[i][i-j],  f[j][i]);
39      }
40      int ans1 = 0;
41      for (int i = 1; i <= n; ++i)
42           add(ans1, ans1, f[i][n]);
43      for (int i = 0; i <= n; ++i)
44          for (int j = i; j <= n; ++j) f[i][j] = Inf;
45      for (int i = 1; i <= n; ++i)
46            f[1][i] = 1;
47      for (int i = 0; i <= n; ++i)
48          for (int j = 0; j <= n; ++j) c[i][j] = Inf;
49      c[1][1] = 1;
50      for (int i = 2; i <= n; ++i){
51           for (int j = i; j > 1; --j) if (s[j] == '1'){
52                 f[j][i] =  min(f[j][i], c[j-1][min(i-j, j-1)] + 1);
53                 if (bigger(j, j-1-(i-j), i-j+1))
54                      f[j][i] = min(f[j][i], f[j-1-(i-j)][j-1] + 1);
55           }
56           for (int j = i; j >= 1; --j)
57                     c[i][i-j+1] = min(c[i][i-j], f[j][i]);
58      }
59      int ans2 = Inf, x;
60      for (int i = n; i >= 1; --i) if (f[i][n] < Inf){
61             if (n - i > 20) break;
62             x = 0;
63             for (int j = i; j <= n; ++j)
64                 x = (x << 1) + s[j] - '0';
65             ans2 = min(ans2, x+f[i][n]);
66      }
67      if (ans2==Inf)
68            for (int i = n-20; i >= 1; --i) if (f[i][n] < Inf){
69                x = 0;
70                for (int j = i; j <= n; ++j){
71                     x = (x << 1) + s[j] - '0';
72                     if (x >= M) x-=M;
73                }
74                ans2 = (x + f[i][n]) % M;
75                break;
76            }
77      printf("%d\n%d\n", ans1, ans2 % M);
78 }
79 
80 int main(){
81     while (scanf("%s", s+1) != EOF){
82          solve();
83     }
84 }
View Code
相關文章
相關標籤/搜索