1. 程式人生 > 實用技巧 >「考前日誌」11.17

「考前日誌」11.17

總結

還是不對勁

打不起精神頭來

而且電腦還巨卡

⑨⑨⑤⑧

今日已完成

  • AcWing291 蒙德里安的夢想

    狀壓DP。
    以行數以及此行的形態為狀態。
    \(f_{i,j}\) 表示前 \(i\) 行,第 \(i\) 行形態為 \(j\) 時的方案總數。
    此處 \(j\) 是一個用十進位制整數記錄的 \(m\) 位二進位制數。
    如果 \(j\) 二進位制下當前位置為 \(1\),說明該位置為某個小長方形的上半部分,下一行的當前位置一定要放下半部分(即為 \(0\))。
    如果為 \(0\) 表示其他情況,對下一行的形態無影響,但要保證連續的 \(0\) 的個數為偶數個。
    對於當前行 \(i\) 的形態 \(j\)

    ,可以由上一行 \(i- 1\) 的形態 \(k\) 轉移過來當且僅當:

    • 當前行的形態 \(j\) 與上一行的形態 \(k\) 的與運算結果為 \(0\)
      這樣保證了上一行形態中為 \(1\) 的位對應的當前位一定為 \(0\),滿足上述條件。
    • \(j\)\(k\) 的按位或運算的二進位制表示中連續 \(0\) 的個數為偶數個。
      這樣也就說明\(j\)\(k\) 的二進位制表示中連續 \(0\) 的個數為偶數個。

    預處理合法(即連續 \(0\) 為偶數)的狀態,然後 dp 即可。

    \[f_{i,j}=\sum\limits_{j\&k=0且j|k合法}f_{i-1,k} \]

    #include <map>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ll long long
    using namespace std;
    
    const int A = 1e5 + 11;
    const int B = 1e6 + 11;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
      char c = getchar(); int x = 0, f = 1;
      for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
      for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
      return x * f;
    }
    
    int n, m;
    bool ok[A];
    ll f[12][1 << 11];
    
    int main() {
      while (cin >> n >> m) {
        if (n == 0 && m == 0) return 0;
        memset(f, 0, sizeof(f));
        for (int i = 0; i < (1 << m); i++) {
          bool cnt = 0, has_odd = 0;
          for (int j = 0; j < m; j++) 
            if (i >> j & 1) has_odd |= cnt, cnt = 0;
            else cnt ^= 1;
          ok[i] = !(has_odd | cnt);
        }
        f[0][0] = 1;
        for (int i = 1; i <= n; i++) {
          for (int j = 0; j < (1 << m); j++) {
            for (int k = 0; k < (1 << m); k++) 
              if ((j & k) == 0 && ok[j | k]) 
                f[i][j] += f[i - 1][k];
          }
        }
        cout << f[n][0] << '\n';
      }
    }
    
  • AcWing289 環路運輸

    環形DP
    還是斷環成鏈的操作
    把環拆開,複製一倍,形成一個長度為 \(2n\) 的鏈
    那麼就是要求最大的 \(1\le{i,j}\le{2n}\)\({i-j}\le{\dfrac{n}{2}}\)\(i,j\)\(a_i+a_j+i-j\) 的最大值
    可以用單調佇列優化,做到 \(O(n)\) 的複雜度

    #include <map>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int A = 2e6 + 11;
    const int B = 1e6 + 11;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
      char c = getchar(); int x = 0, f = 1;
      for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
      for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
      return x * f;
    }
    
    int n, head, tail, len, a[A], q[A], ans;
    
    int main() {
      n = read();
      for (int i = 1; i <= n; i++) a[i] = read(), a[i + n] = a[i];
      len = n / 2, head = 1, tail = 0;
      q[++tail] = a[1];
      for (int i = 2; i <= n * 2; i++) {
        while (head <= tail && q[head] < i - len) head++;
        ans = max(ans, i + a[i] + a[q[head]] - q[head]);
        while (head <= tail && a[q[tail]] - q[tail] < a[i] - i) tail--;
        q[++tail] = i;
      }
      cout << ans << '\n';
      return 0;
    }