「BZOJ 2809」「APIO 2012」Dispatching「啟發式合並」
阿新 • • 發佈:2019-02-15
cst 得到 sin ack strong tro while 滿足 algo 一個點,\(97pts\):
題意
給定一個\(1\)為根的樹,每個點有\(c,w\)兩個屬性,你需要從某個點\(u\)子樹裏選擇\(k\)個點,滿足選出來的點\(\sum_{i=1}^k w(i)\leq m\),最大化\(k\times c(u)\)
題解
可以啟發式合並\(splay\)來做,\(\text{dfs}\)每個點,每次和兒子的\(splay\)合並,就得到了一個維護這個點子樹的平衡樹。再用這個點的\(w\)(題目中的領導力)乘以子樹中最多選多少結點滿足\(c\)(薪水)和\(\leq m\)
肯定貪心選,小的先選
然後這玩意我一開始腦抽寫了二分\(+kth\)的兩個\(\log\)做法,會\(\text{TLE}\)
int kth(int u, int k) { while(1) { if(sz[ch[u][0]] >= k) u = ch[u][0]; else { k -= sz[ch[u][0]] + 1; if(k <= 0) break ; u = ch[u][1]; } } splay(u); return u; } ll qsum(int &u, int k) { u = kth(u, k); return sum[ch[u][0]] + w[u]; } ll query(int &u) { int l = 1, r = sz[u], ans = 0; while(l <= r) { int mid = (l + r) >> 1; if(qsum(u, mid) <= m) l = (ans = mid) + 1; else r = mid - 1; } return ans; }
後來發現直接遞歸找就行了,十分簡單,從左子樹往自己往右子樹依次判能不能選進去
最後的代碼就是這樣了qwq:
#include <algorithm> #include <cstdio> #include <vector> using namespace std; typedef long long ll; const int N = 2e5 + 10; int n, m, w[N], c[N]; int fa[N], sz[N], ch[N][2]; vector<int> G[N]; ll ans, sum[N]; int dir(int u) { return ch[fa[u]][1] == u; } void upd(int u) { sum[u] = sum[ch[u][0]] + sum[ch[u][1]] + w[u]; sz[u] = sz[ch[u][0]] + sz[ch[u][1]] + 1; } void rotate(int u) { int d = dir(u), f = fa[u]; if(fa[u] = fa[f]) ch[fa[u]][dir(f)] = u; if(ch[f][d] = ch[u][d ^ 1]) fa[ch[f][d]] = f; fa[ch[u][d ^ 1] = f] = u; upd(f); upd(u); } void ins(int &rt, int u, int f = 0) { if(!rt) { rt = u; fa[u] = f; sz[u] = 1; sum[u] = w[u]; ch[u][0] = ch[u][1] = 0; return ; } ins(ch[rt][w[u] > w[rt]], u, rt); upd(rt); } void splay(int u) { for(; fa[u]; rotate(u)) if(fa[fa[u]]) rotate(dir(u) == dir(fa[u]) ? fa[u] : u); } ll query(int u, ll s) { if(!u || !s) return 0; if(sum[u] <= s) return sz[u]; if(sum[ch[u][0]] >= s) return query(ch[u][0], s); if(sum[ch[u][0]] + w[u] == s) return sz[ch[u][0]] + 1; if(sum[ch[u][0]] + w[u] > s) return sz[ch[u][0]]; return sz[ch[u][0]] + 1 + query(ch[u][1], s - sum[ch[u][0]] - w[u]); } int a[N], l; void get(int u) { if(u) { get(ch[u][0]); a[++ l] = u; get(ch[u][1]); } } int merge(int u, int v) { if(sz[u] < sz[v]) swap(u, v); l = 0; get(v); int rt = u; for(int i = 1; i <= l; i ++) { ins(rt, a[i]); splay(rt = a[i]); } return rt; } int dfs(int u) { int rt = u; sz[u] = 1; sum[u] = w[u]; fa[u] = ch[u][0] = ch[u][1] = 0; for(int i = 0; i < G[u].size(); i ++) rt = merge(rt, dfs(G[u][i])); ans = max(ans, c[u] * query(rt)); return rt; } int main() { scanf("%d%d", &n, &m); for(int f, i = 1; i <= n; i ++) { scanf("%d%d%d", &f, &w[i], &c[i]); G[f].push_back(i); } dfs(1); printf("%lld\n", ans); return 0; }
「BZOJ 2809」「APIO 2012」Dispatching「啟發式合並」