CF570D Tree Requests 題解 樹上啟發式合併
阿新 • • 發佈:2020-11-05
題目連結:https://codeforces.com/problemset/problem/570/D
解題思路:
樹上啟發式合併。
我一開始開了一個 \(cnt[maxn][26]\) 和一個 \(odd[maxn]\),其中:
- \(cnt[d][c]\) 表示字元 \(c\) 在第 \(d\) 層出現的次數;
- \(odd[d]\) 表示在第 \(d\) 層出現次數為奇數的字元數。
顯然,如果當前節點 \(u\) 對應的子樹中的 \(odd[d] \gt 1\),就沒有辦法構成迴文串;如果 \(odd[u] \le 1\),就可以構成迴文串。
後來看了一下題解,發現只需要開一個 \(cnt[d]\)
示例程式碼:
#include <bits/stdc++.h> using namespace std; const int maxn = 500050; int n, m, sz[maxn], c[maxn], dep[maxn], cnt[maxn]; bool big[maxn]; vector<int> g[maxn]; char s[maxn]; struct Query { int d; bool ans; } query[maxn]; vector<int> qid[maxn]; void getsz(int u, int d) { sz[u] ++; dep[u] = d; for (auto v: g[u]) getsz(v, d+1), sz[u] += sz[v]; } void add(int u, int x) { int d = dep[u]; cnt[d] ^= (1<<c[u]); for (auto v: g[u]) if (!big[v]) add(v, x); } void dfs(int u, bool keep) { int mx = -1, bigSon = -1; // mx表示重兒子的sz, bigSon表示重兒子編號 for (auto v: g[u]) if (sz[v] > mx) mx = sz[ bigSon = v ]; for (auto v: g[u]) if (v != bigSon) dfs(v, false); if (bigSon != -1) dfs(bigSon, true), big[bigSon] = true; add(u, 1); // to do for (auto id: qid[u]) query[id].ans = (__builtin_popcount(cnt[query[id].d]) <= 1); if (bigSon != -1) big[bigSon] = false; if (!keep) add(u, -1); } int main() { scanf("%d%d", &n, &m); for (int i = 2; i <= n; i ++) { int p; scanf("%d", &p); g[p].push_back(i); } scanf("%s", s+1); for (int i = 1; i <= n; i ++) c[i] = s[i] - 'a'; for (int i = 0; i < m; i ++) { int u; scanf("%d%d", &u, &query[i].d); qid[u].push_back(i); } getsz(1, 1); dfs(1, false); for (int i = 0; i < m; i ++) { puts(query[i].ans ? "Yes" : "No"); } return 0; }