人生A了的第一道左偏树题。
题面太长了,概括一下:给定一棵 n 个点的有根树,每个点有两个属性 Ci 与 Li,现在你要指定一个点 R,并在 R的子树内选取若干点(可以选取 R 自己),使得这些点的 Ci 的和不超过 M,而一个选取方案的价值为选取人数 * LR,求选取方案的最大价值。
假设现在已经确定了R,那么我们应该在R的子树内部选取尽量多的点,又因为每一个点对答案的贡献是一样的,所以应该选Ci 尽量小的点,那么对于树中的每一个节点,维护一个大根堆,如果这个堆的sum > m,就删除堆顶元素,直到sum < m。一个节点的堆可以有他的子节点的堆合并而来,所以这个堆不仅要支持删除,还要支持合并,那么就用左偏树吧。
总结一下:每一个节点维护一个左偏树,左偏树中还要维护节点个数和ΣCi,然后自底往上合并堆,并用这个节点的答案更新答案。

1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxn = 1e5 + 5; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ' '; 25 while(!isdigit(ch)) {last = ch; ch = getchar();} 26 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 27 if(last == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar('-'); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 37 int n; 38 ll m; 39 int c[maxn], l[maxn]; 40 struct Edge 41 { 42 int nxt, to; 43 }e[maxn]; 44 int head[maxn], ecnt = 0; 45 void addEdge(int x, int y) 46 { 47 e[++ecnt] = (Edge){head[x], y}; 48 head[x] = ecnt; 49 } 50 51 int root[maxn], ls[maxn], rs[maxn], dis[maxn], v[maxn]; //大根堆 52 ll sum[maxn], ans = 0; 53 int siz[maxn]; 54 55 int merge(int x, int y) 56 { 57 if(!x || !y) return x | y; 58 if(v[x] < v[y]) swap(x, y); 59 rs[x] = merge(rs[x], y); 60 if(dis[ls[x]] < dis[rs[x]]) swap(ls[x], rs[x]); 61 dis[x] = dis[rs[x]] + 1; 62 siz[x] = siz[ls[x]] + siz[rs[x]] + 1; //跟线段树有点像,这个节点的信息从子节点更新而来 63 sum[x] = sum[ls[x]] + sum[rs[x]] + v[x]; 64 return x; 65 } 66 int Del(int x) 67 { 68 return merge(ls[x], rs[x]); 69 } 70 71 void newNode(int now) 72 { 73 sum[now] = v[now] = c[now]; 74 siz[now] = 1; root[now] = now; 75 } 76 void dfs(int now) 77 { 78 newNode(now); 79 for(int i = head[now]; i; i = e[i].nxt) 80 { 81 dfs(e[i].to); 82 root[now] = merge(root[now], root[e[i].to]); 83 } 84 while(sum[root[now]] > m && siz[root[now]]) root[now] = Del(root[now]); 85 ans = max(ans, (ll)siz[root[now]] * (ll)l[now]); //别忘加ll 86 } 87 88 int main() 89 { 90 n = read(); m = read(); 91 for(int i = 1; i <= n; ++i) 92 { 93 int x = read(); c[i] = read(); l[i] = read(); 94 if(x) addEdge(x, i); 95 } 96 dfs(1); 97 write(ans); enter; 98 return 0; 99 }