[APIO2012]派遣

嘟嘟嘟

 

人生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 }
View Code

 

posted @ 2018-10-16 17:15  mrclr  阅读(250)  评论(0编辑  收藏  举报