uva 12161 Ironman Race in Treeland 點分治
阿新 • • 發佈:2019-01-08
題目大意:一棵節點數為n的樹,每條邊有一個長度l和一個花費d,求一條路徑,使得路徑的總花費小於給定的m,且總長度最大
還是點分治,將這個無根樹轉為有根樹之後,一條路徑要麼完全在某一子樹下,要麼經過根,子樹的問題可以遞迴解決,
現在看經過根的情況,按序處理每一棵子樹v,通過dfs可以得到該子樹v所有節點到根的路徑長度和花費,然後需要得到子樹v這個集合
每一個點和已處理過子樹的集合u內每一個點兩兩組合以更新答案,觀察到如果集合v中路徑a的花費高於b,但是a的
長度卻小於b的時候,a一定不會在最優解中,因此可以將a刪除,刪除之後按花費排序後的長度也是有序的,因此可以列舉u中每一個
節點,去v中二分花費,然後更新最優長度,這樣每層的複雜度還是nlogn,按重心點分治得到的是logn層,所以總的複雜度是nlog^2n
#include <cstdio> #include <vector> #include <utility> #include <algorithm> #include <cstring> using namespace std; struct node { int v, d, l; }; //儲存樹中的節點 const int INF = 0x3f3f3f3f; const int maxn = 3e4 + 10; int n, m, t, root, allnode, ans; int maxson[maxn], sz[maxn], vis[maxn], depth[maxn], cost[maxn]; //maxson[i]: 去除節點i後得到的森林中節點數最多的樹的節點 //sz:儲存子樹的節點數 //depth記錄長度,cost記錄花費 vector<pair<int, int> > left, right; //left為處理過的子樹中點的集合,right為當前處理的子樹的點的集合 vector<node> G[maxn]; //建圖 //找重心 void find_root(int u, int fa) { sz[u] = 1; maxson[u] = 0; for (int i = 0; i < G[u].size(); i++) { node x = G[u][i]; int v = x.v; if (v == fa || vis[v]) continue; find_root(v, u); sz[u] += sz[v]; maxson[u] = max(maxson[u], sz[v]); } maxson[u] = max(maxson[u], allnode - sz[u]); if (maxson[root] > maxson[u]) root = u; } //計運算元樹所有節點的長度和花費 void dfs(int u, int fa) { sz[u] = 1; right.push_back({cost[u], depth[u]}); for (int i = 0; i < G[u].size(); i++) { node x = G[u][i]; int v = x.v; int d = x.d; int l = x.l; if (v == fa ||vis[v]) continue; depth[v] = depth[u] + l; cost[v] = cost[u] + d; dfs(v, u); sz[u] += sz[v]; } } void calc(int u) { left.clear(); left.push_back({0, 0}); sz[u] = 1; for (int i = 0; i < G[u].size(); i++) { node x = G[u][i]; int v = x.v; if (vis[v]) continue; right.clear(); depth[v] = x.l; cost[v] = x.d; dfs(v, u); sz[u] += sz[v]; sort(right.begin(), right.end()); //這裡沒有顯示刪除,而是將需要刪除的節點的路徑和長度賦值為和上一個節點 for (int j = 1; j < right.size(); j++) { if (right[j].second <= right[j - 1].second) { right[j].second = right[j - 1].second; right[j].first = right[j - 1].first; } } for (int j = 0; j < left.size(); j++) { if (left[j].first > m) continue; int pos = upper_bound(right.begin(), right.end(), pair<int, int>(m - left[j].first, INF)) - right.begin() - 1; if (pos < 0) continue; if (right[pos].second + left[j].second > ans) ans = right[pos].second + left[j].second; } for (int j = 0; j < right.size(); j++) { left.push_back(right[j]); } } } void solve(int u) { left.clear(); right.clear(); calc(u); vis[u] = 1; for (int i = 0; i < G[u].size(); i++) { node x = G[u][i]; int v = x.v; if (vis[v]) continue; allnode = sz[v]; root = 0; find_root(v, u); solve(root); } } int main() { scanf("%d", &t); int cas = 0; while (t--) { cas ++; scanf("%d%d", &n, &m); left.reserve(n); right.reserve(n); memset(vis, 0, sizeof(vis)); maxson[0] = n; for (int i = 1; i <= n; i++) G[i].clear(); int u, v, d, l; for (int i = 1; i < n; i++) { scanf("%d%d%d%d", &u, &v, &d, &l); G[u].push_back({v, d, l}); G[v].push_back({u, d, l}); } root = ans = 0; allnode = n; find_root(1, -1); solve(root); printf("Case %d: %d\n", cas, ans); } return 0; }