微軟釋出新款粉色 Xbox 手柄
阿新 • • 發佈:2022-05-04
介紹
點分治是用來解決樹上路徑問題的一種方法。
在解決樹上路徑問題時,我們可以選取一點為根,將樹轉化為有根樹,然後考慮經過根的所有路徑(有時將兩條從根出發的路徑連線為一條)。統計完這些路徑的答案後,將根節點標記為刪除,對剩下的若干棵樹進行同樣的操作。
如圖,我們可以先考慮經過節點 1 的路徑,之後將節點 1 標記為刪除,此時可以認為考慮過的路徑均已被刪除。繼續對其它子樹做相同處理即可。
每次確認一個根節點後,共有 n 條需要考慮的路徑( n 為當前子樹大小)。上圖中將 1 刪除後,剩下左側的子樹較大,和原樹大小相當,繼續處理這棵子樹時仍然需要與前一過程相當的時間。
最嚴重的情況,當整棵樹是一條鏈時,每次需要考慮的路徑數量是 O(n) 級別的,如果每條路徑需要常數時間進行統計,則總時間複雜度為 O(n^2) 。而對於形態隨機的樹,則遠遠小於這個級別。
如果我們選擇 5 作為這棵樹的根節點,情況會好很多 —— 刪除 5 後剩餘的最大一棵子樹的大小比刪除 1 時要小。這說明「科學地」選擇點作為根節點可以有效的降低複雜度。
重心方面操作
模板
struct Node { struct Edge *firstEdge; // solved 表示該節點是否已被解決 // 在點分治中,標記 solved 的節點被認為不存在 // // vis 表示在當前 DFS / BFS 中是否訪問過 bool solved, vis; // size 表示子樹大小(和樹剖中相同) // max 表示最大子節點大小 int size, max; Node *fa; // 父節點 } N[MAXN + 1]; // 找以 start 為根的子樹的重心 // 非遞迴 DFS inline Node *center(Node *start) { std::stack<Node *> s; s.push(start); start->vis = false; start->fa = NULL; static Node *a[MAXN + 1]; // 儲存所有 DFS 到的節點 int cnt = 0; while (!s.empty()) { Node *v = s.top(); // 如果是第一次出棧,則不將 v 從棧中刪除 // 將所有子節點入棧 if (!v->vis) { a[++cnt] = v; // 記錄節點 v->vis = true; for (Edge *e = v->firstEdge; e; e = e->next) { // 判斷不走回父節點,不走到已經 solved 的節點 if (e->to != v->fa && !e->to->solved) { e->to->fa = v; e->to->vis = false; // 子節點入棧 s.push(e->to); } } } else { // 第二次出棧,表示回溯到 v v->size = 1; v->max = 0; for (Edge *e = v->firstEdge; e; e = e->next) { if (e->to->fa == v) { // 維護 size 和 max v->size += e->to->size; v->max = std::max(v->max, e->to->size); } } // 將 v 從棧中刪除 s.pop(); } } // 統計重心 Node *res = NULL; for (int i = 1; i <= cnt; i++) { // v->max 表示在整棵子樹中,刪掉 v 後剩餘的最大子樹 // 如果把 v 作為根,則原有的除 v 的子樹以外的部分會成為 v 的一棵子樹 // 這部分的大小為 總節點數量 - v->size // 因為是以 start 作為根進行的 BFS,總節點數量即為 start->size a[i]->max = std::max(a[i]->max, start->size - a[i]->size); // 更新答案 if (!res || res->max > a[i]->max) res = a[i]; } return res; } // 主求解過程 inline int solve() { std::stack<Node *> s; s.push(&N[1]); int ans = 0; // 答案 while (!s.empty()) { // 這裡的 DFS 不需要回溯,所以每次出棧即可 Node *v = s.top(); s.pop(); // 求重心 Node *root = center(v); // 為防止後續的 BFS、DFS 走回根,先將根置為 solved root->solved = true; ans += calc(root); } return ans; }