[HEOI2015]兔子与樱花

https://zybuluo.com/ysner/note/1218440

题面

一棵\(n\)个点带点权树上,所有点点权和儿子数之和不能超过\(m\)。每删去一结点,它的父亲将继承该结点的权值和所有儿子。问在满足要求的前提下能删多少个点。

  • \(n\leq2*10^6,m\leq10^5\)

解析

删去一个结点,好处不用说,坏处是父亲可能不能被删了。但是,它父亲的父亲不会受到影响。所以删点顶多以一换一,稳赚不赔。
为了使删点数尽可能多,在每个结点下对儿子贡献排个序,从小取到大即可。

注意统计儿子贡献过程不能与向下递归步骤写在一起。这样下面的贡献会覆盖上面的。

il void dfs(re int u,re int fa)
{
  int sta[1000],top=0;c[u]+=k[u];
  for(re int i=h[u];i+1;i=e[i].nxt)
    {
      re int v=e[i].to;
      if(v==fa) continue;
      dfs(v,u);
      sta[++top]=c[v]-1; 
    }
  sort(sta+1,sta+top+1);
  fp(i,1,top) if(c[u]+sta[i]<=m) c[u]+=sta[i],ans++;
  else break;
}

下面是对比,咕咕咕

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define re register
#define il inline
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=2e6+100;
struct Edge{int to,nxt;}e[N<<1];
int n,m,c[N],k[N],h[N],cnt,sz[N],ans,sta[N];
il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
il ll gi()
{
  re ll x=0,t=1;
  re char ch=getchar();
  while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  if(ch=='-') t=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  return x*t;
}
/*强调
il void dfs(re int u)
{
  int top=0;c[u]+=k[u];
  for(re int i=h[u];i+1;i=e[i].nxt)
    {
      re int v=e[i].to;
      dfs(v); 
    }
  for(re int i=h[u];i+1;i=e[i].nxt)
    {
      re int v=e[i].to;
      sta[++top]=c[v]-1;
    }
  sort(sta+1,sta+top+1);
  fp(i,1,top) if(c[u]+sta[i]<=m) c[u]+=sta[i],ans++;
  else break;
}
*/
int main()
{
  memset(h,-1,sizeof(h));
  n=gi();m=gi();
  fp(i,1,n) c[i]=gi();
  fp(i,1,n)
    {
      k[i]=gi();
      fp(j,1,k[i])
    {
      re int v=gi()+1;
      add(i,v);
    }
    }
  dfs(1);
  printf("%d\n",ans);
  return 0;
}
posted @ 2018-07-19 00:08  小蒟蒻ysn  阅读(176)  评论(0编辑  收藏  举报