Luogu P1552 [APIO2012]派遣【左偏树】By cellur925

题目传送门

$Chat$

哈哈哈我xj用dfs序乱搞竟然炸出了66分....(其实还是数据水,逃)


$Sol$

首先我们应该知道,一个人他自己的满意度与他子树所有节点的领导力是无关的,一个人的满意度受它子树影响只通过选子树的数量来体现。

因为薪水预算是有限的,而我们又想获得更多的子树,那么我们肯定想要子树薪水排名前$k$个的(满足不超过总预算)。我的暴力想法是每次排序来维护的,其实这里正解是用左偏树来维护的。(嘤我只写过左偏树板子而且不太理解)

我们每次都尽量把一个节点的所有子树都选上,然后每次用可并堆找出花费最大的点干掉(即维护大根堆),直到满足条件。为什么要用可并堆呢?在这个树形结构中,根(最大的)被干掉后,我们要把它的左儿子和右儿子合并。这个时候用左偏树再合适不过了。

上述算法的实现用到了树形dp(递归)的思想。


$Code$

因为没写过非板子的左偏树题目...所以这里写清楚些。$fa[]$记录的是这个点的根。开始时这个节点的根等于它自己。$merge$函数返回的也是根。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define maxn 100090
 4 
 5 using namespace std;
 6 typedef long long ll;
 7 
 8 int n,m,tot;
 9 int head[maxn],fa[maxn],dis[maxn],lch[maxn],rch[maxn],size[maxn];
10 ll ans,v[maxn],val[maxn],sum[maxn];
11 int T[maxn][2];
12 struct node{
13     int to,next;
14 }edge[maxn*2];
15 
16 void add(int x,int y)
17 {
18     edge[++tot].to=y;
19     edge[tot].next=head[x];
20     head[x]=tot;
21 }
22 
23 int merge(int x,int y)
24 {
25     if(x==0||y==0) return x+y;
26     if(v[x]<v[y]||(v[x]==v[y]&&x>y)) swap(x,y);
27     rch[x]=merge(rch[x],y);
28     fa[rch[x]]=y;
29     if(dis[lch[x]]<dis[rch[x]]) swap(lch[x],rch[x]);
30     dis[x]=dis[rch[x]]+1;
31     return x;
32 }
33 
34 void dfs(int u,int f)
35 {
36     fa[u]=u;size[u]=1;sum[u]=v[u];
37     for(int i=head[u];i;i=edge[i].next)
38     {
39         int v=edge[i].to;
40         if(v==f) continue;
41         dfs(v,u);
42         size[u]+=size[v];sum[u]+=sum[v];
43         fa[u]=merge(fa[u],fa[v]);
44     }
45     while(sum[u]>m&&size[u])
46     {
47         sum[u]-=v[fa[u]];
48         size[u]--;
49         fa[u]=merge(lch[fa[u]],rch[fa[u]]);
50     }
51     ans=max(ans,1ll*size[u]*val[u]);
52 }
53 
54 int main()
55 {
56     scanf("%d%d",&n,&m);
57     for(int i=1;i<=n;i++)
58     {
59         int x=0;scanf("%d",&x);
60         add(i,x);add(x,i);
61         scanf("%lld%lld",&v[i],&val[i]);
62     }
63     dfs(1,0);
64     printf("%lld",ans);
65     return 0;
66 }
View Code

猜你喜欢

转载自www.cnblogs.com/nopartyfoucaodong/p/9748682.html
今日推荐