zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    POJ1064 浮点数二分答案
    2019牛客暑期多校训练营(第二场)H.Second Large Rectangle
    最大全1子矩阵的两种解法(例题:City Game HDU
    POJ
    Codeforces Round #588 (Div. 2) C
    1216E
    1221D
    BUY LOW, BUY LOWER, POJ
    Priest John's Busiest Day (2-sat)
    poj1080
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9748682.html
Copyright © 2011-2022 走看看