zoukankan      html  css  js  c++  java
  • 【APIO2012T1】派遣-贪心+左偏树

    测试地址:派遣

    做法:我们知道,对于每一棵子树,我们都贪心选里面费用最小的一些点,一直选直到如果再选费用就超限为止,这时以这棵子树的根为管理者的最优解显然是根的领导力乘上选的点数。但是,如果对于每棵子树都排序选择的话,时间复杂度O(N^2*logN),即使使用归并排序时间复杂度也达到O(N^2),不能满足要求。但我们可以换一种思路,对于每棵子树建一个大根堆,表示在这颗子树上贪心应选择的点,键值为费用,设这个集合为Q。我们知道如果在一棵子树中不选的点,在更上层的子树中也同样不会被选,所以可以把这些点直接抛弃。那么对于一个节点,先把它所有子节点的Q和当前节点合并,如果加起来的费用超过限制,则逐个删掉堆顶节点直到不超过限制为止。合并堆我们可以用左偏树O(logN)复杂度解决,每个节点的Q至多被合并一次,总复杂度就是O(NlogN),而每个点至多入堆和出堆一次,删除的总复杂度为O(NlogN),所以这个算法的复杂度为O(NlogN),可以解决这个问题。

    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ll long long
    using namespace std;
    int n,rt[100010],first[100010]={0},tot=0;
    ll m,l[100010],ans=0;
    struct leftist
    {
      int lc,rc,d;
      ll c,s,h;
    }nd[100010];
    struct edge {int v,next;} e[100010];
    
    void insert(int a,int b)
    {
      e[++tot].v=b,e[tot].next=first[a],first[a]=tot;
    }
    
    int find(int x)
    {
      int r=x,i=x,j;
      while(r!=rt[r]) r=rt[r];
      while(i!=r) j=rt[i],rt[i]=r,i=j;
      return r;
    }
    
    int merge(int x,int y)
    {
      if (!x) return y;
      if (!y) return x;
      if (nd[x].c<nd[y].c) swap(x,y);
      nd[x].rc=merge(nd[x].rc,y);
      int lft=nd[x].lc,rht=nd[x].rc;
      rt[rht]=x;
      if (nd[lft].d<nd[rht].d) swap(nd[x].lc,nd[x].rc);
      nd[x].s=nd[nd[x].lc].s+nd[nd[x].rc].s+nd[x].c;
      nd[x].h=nd[nd[x].lc].h+nd[nd[x].rc].h+1;
      nd[x].d=nd[nd[x].rc].d+1;
      return x;
    }
    
    int del(int x)
    {
      int lft=nd[x].lc,rht=nd[x].rc;
      nd[x].lc=nd[x].rc=0;
      nd[x].s=nd[x].c,nd[x].h=1;
      rt[lft]=lft,rt[rht]=rht,rt[x]=x;
      return merge(lft,rht);
    }
    
    void dfs(int v)
    {
      for(int i=first[v];i;i=e[i].next)
      {
        dfs(e[i].v);
    	rt[v]=merge(find(v),find(e[i].v));
      }
      while(nd[rt[v]].s>m)
      {
        rt[v]=del(rt[v]);
      }
      ans=max(ans,l[v]*nd[rt[v]].h);
    }
    
    int main()
    {
      scanf("%d%lld",&n,&m);
      for(int i=1;i<=n;i++)
      {
        int b;
        scanf("%d%lld%lld",&b,&nd[i].c,&l[i]);
    	nd[i].lc=nd[i].rc=nd[i].d=0;
    	rt[i]=i;nd[i].s=nd[i].c;nd[i].h=1;
    	if (b) insert(b,i);
      }
      nd[0].d=-1;nd[0].s=nd[0].h=0;
      
      dfs(1);
      
      printf("%lld",ans);
      
      return 0;
    }
    


  • 相关阅读:
    CompletableFuture(yet)
    模拟future
    一次使用jmap评估是否可以把类似session信息挂靠在某未知框架的某暴露对象上
    只读事务与普通读
    多重分表分库一般解决方案
    mat解决oom一般方法实践
    类加载器隔离朴实案例(二)logback
    在51系列中data,idata,xdata,pdata的区别
    linux 简单的DMA例程
    disable_irq()与disable_irq_nosync()区别
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793777.html
Copyright © 2011-2022 走看看