「考前日志」11.14


學到了什么

學到了虛脫

給學弟調樹剖沒調出來,我太菜了

  • 斷環成鏈的方法:任意選擇一個位置斷開,復制形成 2 倍長度的鏈。
  • 區間DP的小套路
  • 學文化課真爽

今日已完成

  • 一節文化課:萬能公式秒殺電容器動態分析
    \(E=\dfrac{U}{d}=\dfrac{Q}{\varepsilon{S}}\)
    如果再算上 \(C=\dfrac{Q}{U}\) 就有七個物理量。
    無腦帶公式即可。
    應用有

    1. 判斷受力
    2. 判斷場強
    3. 判斷靜電計張角(看 \(U\)
    4. 判斷電容
    5. 定量計算
  • AcWing282 石子合並

    區間DP基礎題

    int n, m, f[A][A], sum[A], a[A]; 
    
    int main() {
      n = read();
      for (int i = 1; i <= n; i++) 
        a[i] = read(), sum[i] = sum[i - 1] + a[i];
      memset(f, inf, sizeof(f));
      for (int i = 1; i <= n; i++) f[i][i] = 0;
      for (int len = 2; len <= n; len++) {
        for (int l = 1; l <= n - len + 1; l++) {
          int r = min(l + len - 1, n);
          for (int k = l; k < r; k++) {
            f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r]);
          }
          f[l][r] += sum[r] - sum[l - 1];
        }
      }
      cout << f[1][n] << '\n';
      return 0;
    }
    
  • AcWing283 多邊形
    區間 DP,斷環成鏈降低時間復雜度。

    char s;
    int n, ys[A], w[A], f[2][A][A];
    
    inline void checkmax(int &x, int y) { x = x < y ? y : x; }
    inline void checkmin(int &x, int y) { x = x < y ? x : y; }
    
    int main() {
      n = read();
      for (int i = 1; i <= n; i++) {
        cin >> s >> w[i];
        //x乘,t加 
        if (s == 't') ys[i] = 0;
        else if (s == 'x') ys[i] = 1;
      } 
      for (int i = n + 1; i <= n * 2; i++)
        w[i] = w[i - n], ys[i] = ys[i - n];
      memset(f[0], 0xcf, sizeof(f[0]));
      memset(f[1], 0x3f, sizeof(f[1]));
      for (int i = 1; i <= n * 2; i++) f[0][i][i] = f[1][i][i] = w[i];
      for (int len = 2; len <= n; len++) {
        for (int l = 1; l <= n * 2 - len + 1; l++) {
          int r = l + len - 1;
          for (int k = l; k < r; k++) {
            checkmax(f[0][l][r], ys[k + 1] == 1 ? f[0][l][k] * f[0][k + 1][r] : f[0][l][k] + f[0][k + 1][r]);
            checkmin(f[1][l][r], ys[k + 1] == 1 ? f[1][l][k] * f[1][k + 1][r] : f[1][l][k] + f[1][k + 1][r]);
            if (ys[k + 1] == 1) {
              checkmax(f[0][l][r], f[0][l][k] * f[0][k + 1][r]);
              checkmax(f[0][l][r], f[1][l][k] * f[1][k + 1][r]);
              checkmax(f[0][l][r], f[0][l][k] * f[1][k + 1][r]);
              checkmax(f[0][l][r], f[1][l][k] * f[0][k + 1][r]);
              checkmin(f[1][l][r], f[0][l][k] * f[0][k + 1][r]);
              checkmin(f[1][l][r], f[1][l][k] * f[1][k + 1][r]);
              checkmin(f[1][l][r], f[1][l][k] * f[0][k + 1][r]);
              checkmin(f[1][l][r], f[0][l][k] * f[1][k + 1][r]);
            }
          }
        }
      }
      int ans = -32768;
      for (int i = 1; i <= n; i++) ans = max(ans, f[0][i][i + n - 1]);
      cout << ans << '\n';
      for (int i = 1; i <= n; i++) {
        if (f[0][i][i + n - 1] == ans) cout << i << " ";
      }
      return 0;
    }
    
  • AcWing284 金字塔

    區間DP,用 \(f_{l,r}\) 表示子串 \(s_{l,r}\) 對應着多少種可能的金字塔結構。

    為了防止重復,只考慮子串 \(s_{l,r}\) 的第一棵子樹是由哪一段構成的,枚舉第一棵子樹的划分點 \(k\),令子串 \(s_{l+1,k-1}\) 作為第一棵子樹,然后令子串 \(s_{k,r}\) 作為剩余部分。因為 \(k\) 不同,所以 \(s_{l+1,r-1}\) 就一定不同,因此就不會產生重復。

    由此得到狀態轉移方程:

    \[f_{l,r}=\begin{cases}0&s_{l}\ne{s_{r}}\\f_{l+1,r-1}+\sum\limits_{l+2\le{k}\le{r-2},s_{k}=s_{l}}f_{l+1,k-1}\times{f_{k,r}}&s_{l}=s_{r}\end{cases} \]

    用記憶化搜索實現。

    char s[A]; 
    int n, f[A][A];
    
    int solve(int l, int r) {
      if (l > r) return 0;
      if (l == r) return f[l][r] = 1;
      if (f[l][r] != -1) return f[l][r];
      if (s[l] != s[r]) return f[l][r] = 0;
      f[l][r] = solve(l + 1, r - 1); 
      for (int k = l + 2; k <= r - 2; k++) {
        if (s[l] == s[k]) 
          f[l][r] = (f[l][r] + 1ll * solve(l + 1, k - 1) * solve(k, r) % mod) % mod; 
      }
      return f[l][r];
    }
    
    int main() {
      scanf("%s", s + 1);
      memset(f, -1, sizeof(f));
      n = strlen(s + 1);
      cout << solve(1, n) << '\n';
    }
    
  • AcWing285 沒有上司的舞會

    樹形DP,找到一個不依賴別人的點作為根,然后進行樹形DP,設 \(f_{i,1/0}\) 表示以 \(i\) 為根的子樹,第 \(i\) 個點選或不選所能獲得的最大價值,那么有:

    \[f_{i,0}=\sum\limits_{to\in{Son(x)}}\max(f_{to,0},f_{to,1}) \]

    \[f_{i,1}=h_i+\sum\limits_{to\in{Son(x)}f_{to,0}} \]

    最后取 \(\max(f_{R,0},f_{R,1})\) 就是答案,\(R\) 是選出的根節點,\(Son(x)\)\(x\) 的子節點的集合。

    struct node { int to, nxt; } e[A << 1];
    int n, head[A], cnt, f[A][2], h[A], du[A], R;
    
    inline void add(int from, int to) {
      e[++cnt].to = to;
      e[cnt].nxt = head[from];
      head[from] = cnt;
    }
    
    void dfs(int x) {
      f[x][1] = h[x], f[x][0] = 0;
      for (int i = head[x]; i; i = e[i].nxt) {
        int to = e[i].to;
        dfs(to);
        f[x][0] += max(f[to][0], f[to][1]);
        f[x][1] += f[to][0];
      }
    }
    
    int main() {
      n = read();
      for (int i = 1; i <= n; i++) h[i] = read();
      for (int i = 1; i < n; i++) {
        int x = read(), y = read();
        du[x] = 1;
        add(y, x);
      }
      for (int i = 1; i <= n; i++) {
        if (!du[i]) {
          R = i;
          break;
        }
      }
      dfs(R);
      cout << max(f[R][0], f[R][1]) << '\n';
      return 0;
    }
    
  • P1895 數字序列
    模擬

    int len[A];
    int main() {
      for (int i = 1; i <= 1e5; i++) len[i] = len[i - 1] + ((int)log10(i) + 1);
      int T = read();
      while (T--) {
        int n = read();
        int k1 = 0, k2 = 0, tot = 0;
        while (++k1) {
          tot += len[k1];
          if (tot >= n) break;
        }
        n -= (tot - len[k1]);
        while (++k2) {
          if (len[k2] >= n) break;
        }
        cout << k2 / (int)pow(10, len[k2] - n) % 10 << '\n';
      }
      return 0;
    }
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM