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;
    }
    


  • 相关阅读:
    什么是长尾关键词?【摘】
    Visual Studio 2010已经发布,简单列举一下新特性,重点学习一下
    增加反向链接的35个技巧【摘】
    鼠标移动上变为手的样式的css,cursor: pointer或者CURSOR: hand
    百度空间地址栏的logo咋成了迅雷的logo了,被攻击后遗症?
    百度优化技巧和方法【摘】
    asp入门之分页
    asp入门之操作数据库
    asp入门之简单介绍
    asp入门之在线编辑器(QQMail HtmlEditor 菜刀版 1.2版)改进版
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793778.html
Copyright © 2011-2022 走看看