zoukankan      html  css  js  c++  java
  • [SDOI2017]苹果树

    题目描述

    https://www.luogu.org/problemnew/show/P3780

    题解

    一道思路巧妙的背包题。

    对于那个奇怪的限制,我们对此稍加分析就可以发现它最后选择的区域是一个包含根节点的联通块。

    对于(t-hleq k)这个限制,我们可以把它看做是可以选择一条从根到某个节点的一条链,在这条链上不耗费任何代价的拿一个苹果,但是再去拿其它苹果是要有代价的。

    根据贪心,我们的链一定是一直选到叶子的,所以我们最后是要枚举岁有的叶子。

    在这个时候我们可以想到在枚举这个叶子的时候,这个叶子把dfs序分成了两半,我们可以预处理出前后缀的背包值,在枚举叶子的时候做一次背包合并。

    然后我们的问题就变成了处理前后缀的背包值。

    后面就比较神仙了,对于正着的部分,我们按照dfs先序遍历依次dp,进入这个节点的时候就把有代价的部分做背包,遍历儿子的时候就往下赋值,往上更新的时候把必须拿的那个更新上。

    这样可以保证遍历到某个叶子的时候,上面的节点还没有动那个免费的苹果。

    对于反着的部分,我们只有在往上更新的时候把免费的和付费的全部更新,这样可以保证到叶子的时候上面什么都没算,就可以放心合并了。

    实现起来细节比较多%了(Claris)的代码。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<cmath>
    #define N 20009
    #define M 500009
    using namespace std;
    typedef long long ll;
    const int maxnum=25600009;
    int tot,head[N],a[N],b[N],dfn1[N],dfn2[N],n,k,siz,fa[N],K;
    int fz[M],q[M],ans;
    int dp1[maxnum],dp2[maxnum];
    inline ll rd(){
    	ll x=0;char c=getchar();bool f=0;
    	while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    	return f?-x:x;
    }
    struct edge{int n,to;}e[N];
    inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
    inline void solve(int *dp,int a,int b){
    	int nowval=0;
    	int h=1,t=0;
    	for(int i=0;i<=k;++i){
    		dp[i]-=nowval;
    		while(h<=t&&dp[q[t]]<=dp[i])t--;
    		q[++t]=i; 
    		while(h<=t&&q[h]+a<i)h++;
    		fz[i]=dp[q[h]]+nowval;
    		nowval+=b;
    	}
    	memcpy(dp,fz,siz);
    }
    void dfs1(int u){
    	int *now=dp1+dfn1[u]*K,nowa=a[u]-1,nowb=b[u];
    	if(nowa)solve(now,nowa,nowb);
    	for(int i=head[u];i;i=e[i].n){
    		int v=e[i].to;
    		dfn1[v]=++dfn1[0];
    		memcpy(dp1+dfn1[v]*K,now,siz);
    		dfs1(v);	
    	}
    	if(fa[u]){
    	   int *now1=dp1+dfn1[fa[u]]*K+1,*now2=dp1+dfn1[u]*K;
    	   for(int j=1;j<=k;++j,now1++,now2++)*now1=max(*now1,*now2+b[u]);
        }
    }
    void dfs2(int u,int val){
    	for(int i=head[u];i;i=e[i].n){
    		int v=e[i].to;
    		dfn2[v]=++dfn2[0];
    		memcpy(dp2+dfn2[v]*K,dp2+dfn2[u]*K,siz);
    	    dfs2(v,val+b[v]);
    	}
    	if(!head[u]){
    		int *now1=dp1+dfn1[u]*K,*now2=dp2+dfn2[u]*K;
    		for(int j=0;j<=k;++j)ans=max(ans,*(now1+j)+*(now2+k-j)+val);;
    	}	
    	int *now1=dp2+dfn2[u]*K,nowa=a[u]-1,nowb=b[u];
    	if(nowa)solve(now1,nowa,nowb);
    	if(fa[u]){	
    	    int *now1=dp2+dfn2[u]*K;
    		int *now2=dp2+dfn2[fa[u]]*K;now2++;
    		for(int j=1;j<=k;++j,now1++,now2++)*now2=max(*now2,*now1+b[u]);///!!!!!!!!!!
    	}
    }
    inline void unit(){
    	memset(dp1,0,sizeof(dp1));
    	memset(dp2,0,sizeof(dp2));
    	memset(head,0,sizeof(head));
    	memset(dfn1,0,sizeof(dfn1));
    	memset(dfn2,0,sizeof(dfn2));
    	tot=ans=0;
    }
    int main(){
    	int T=rd();
    	while(T--){
    	  n=rd();k=rd();K=k+1;
    	  siz=K*sizeof(int);
    	  unit();
    	  for(int i=1;i<=n;++i){
    	  	fa[i]=rd();
    	  	if(fa[i])add(fa[i],i);
    	  	a[i]=rd();b[i]=rd();
    	  }
    	  dfs1(1);
    	  memset(head,0,sizeof(head));
    	  tot=0;
    	  for(int i=n;i>=1;--i)if(fa[i])add(fa[i],i);
    	  dfs2(1,b[1]);
    	  printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    TCP详解——传输控制协议(总述)
    sublime text3使用插件SublimeTextTrans设置透明度
    vue学习1
    js事件绑定/监听
    如何成为顶级程序猿
    HTML5中meta属性
    windows安装SVN服务器并设置开机启动
    oracle安装登录sqlplus / as sysdba然后报错ERROR: ORA-01031 insufficient privileges
    移动端和PC端有什么区别
    div+css通用兼容性代码整理
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10707303.html
Copyright © 2011-2022 走看看