Solution - Atcoder ARC157E XXYX Binary Tree

rizynvu發表於2024-10-02

考慮這個不存在 \(\texttt{YY}\) 的限制,與 \(\texttt{XX}\) 個數為變數的限制相比較,看起來 \(\texttt{Y}\) 就更特殊,於是考慮從 \(\texttt{Y}\) 的視角來分析問題。

同時考慮到因為有 \(A + B + C = n - 1\),所以 \(\texttt{XX}\) 其實也不是很重要,因為只需要讓 \(\texttt{XY}\)\(\texttt{YX}\) 的個數滿足條件就可以了。

注:這之後的分析預設遵守無 \(\texttt{YY}\),即不存在自己與父親皆為 \(\texttt{Y}\) 的情況。

於是可以考慮先讓整顆樹都是 \(\texttt{X}\),把一個 \(\texttt{X}\) 改變成 \(\texttt{Y}\) 的影響。
手玩一下可以知道若為 \(\texttt{Y}\) 的節點的集合為 \(S\),則有 \(B = |S| - [1\in S], C = \sum\limits_{u\in S} |\operatorname{son}_u|\)

再考慮到這題的特殊條件,\(|\operatorname{son}_u| = 2 \operatorname{or} 0\)
那麼就有 \(C = 2|S\cap \complement_{1, \cdots, n} \operatorname{leaf}|\),即 \(S\) 中非葉子結點的個數的 \(2\) 倍。

那麼能發現 \(\frac{C}{2}\) 就是 \(S\) 中非葉子結點的個數,又因為 \(B = |S| - [1\in S]\),那麼就可以知道 \(S\) 中的葉子節點個數為 \(B - \frac{C}{2} + [1\in S]\),對於這個 \([1\in S]\) 因為實際上就涉及 \(2\) 種情況是好處理的。
於是接下來的重點是是否存在 \(x\) 個非葉子節點與 \(y\) 個葉子節點的選取。

那麼一個想法是用一個樹形揹包來合併資訊,記 \(f_{u, y, x, 0 / 1}\)\(u\) 節點子樹內能否存在選了 \(y\) 個葉子節點 \(x\) 個非葉子節點且自身是否選了的方案,但顯然是過不了的。

考慮到因為有 \(2\) 個限制,考慮固定下一邊的限制。
例如考慮固定下葉子節點的個數 \(y\),判定是否存在 \(x\) 個非葉子結點的個數。

能夠發現的是 \(x\) 是具有單調性的,也就是對於同樣的子樹與同樣的 \(y\) 及同樣該節點是否選取的情況,合法的 \(x\) 應當是一個字首的。
即對於同樣的 \(u, x, p\),應存在一個 \(\max x\),滿足 \(f_{u, y, x, p} = [x\le \max x]\)

那麼就可以記 \(f_{u, y, p} = \max x\),用樹形揹包合併即可。

最後記得要討論一下 \([1\in S]\)

時間複雜度 \(\mathcal{O}(n^2)\)

#include<bits/stdc++.h>
constexpr int inf = 1e9;
const int maxn = 1e4 + 10;
int fa[maxn], siz[maxn];
int f[maxn][maxn][2];
int ls[maxn], rs[maxn];
inline void solve() {
   int n, a, b, c; scanf("%d%d%d%d", &n, &a, &b, &c);
   for (int i = 1; i <= n; i++) ls[i] = rs[i] = 0;
   for (int i = 2; i <= n; i++) scanf("%d", &fa[i]), (ls[fa[i]] ? rs[fa[i]] : ls[fa[i]]) = i;
   if (c & 1)
      return puts("No"), void();
   for (int u = n; u; u--) {
      if (! ls[u]) {
         siz[u] = 1;
         f[u][0][0] = f[u][0][1] = 0;
         f[u][1][1] = 0, f[u][1][0] = -inf;
         continue;
      }
      siz[u] = u > 1 ? siz[ls[u]] + siz[rs[u]] : n;
      for (int i = 0; i <= siz[u]; i++)
         for (int j : {0, 1})
            f[u][i][j] = -1e9;
      for (int x : {0, 1})
         for (int i = 0; i <= siz[ls[u]]; i++)
            for (int j = 0; j <= siz[rs[u]]; j++)
               f[u][i + j][x] = std::max(f[u][i + j][x], f[ls[u]][i][x ^ 1] + f[rs[u]][j][x ^ 1] + x);
      if (u > 1)
         for (int i = 0; i <= siz[u]; i++)
            f[u][i][1] = std::max(f[u][i][1], f[u][i][0]);
   }
   puts((b >= c / 2 - 1 && f[1][b - c / 2 + 1][1] >= c / 2)
      || (b >= c / 2 && f[1][b - c / 2][0] >= c / 2) ? "Yes" : "No");
}
int main() {
   int T; scanf("%d", &T);
   while (T--) solve();
   return 0;
}

相關文章