[树上依赖背包] BZOJ4910 LOJ2268: [SDOI2017] 苹果树

首先考虑 thmaxk 的限制。可以看做除去最长到根链上的点各一个,剩下的最多取 k
可以想到枚举叶子,求必选这个叶到根路径上各一个之后,其他取 k 个的最大值。
普通的树上依赖背包一般是:
按后序遍历顺序 DP , fi,j 表示后序遍历前 i 个中选了 j 个的最优解,后序遍历的好处是 i 的子树内的点是 i 前面的连续一段。如果 i 不选,只能从子树外跳过来,否则就能从子树内 (fi1) 转移过来。
考虑这道题,肯定要先 DP 预处理。但由于强制已经选了一条链,可能就会发生一些问题。
关键要进行一个拆点,考虑所有的 i ,新建点 i,连边 ii ai=ai1, ai=1
这样就把枚举取的链和 DP 的东西分开了。即两次DP,其后序遍历儿子顺序相反,然后最后求答案时就是左半边加右半边就行了。 DP 要单调队列优化。

小错误…调了半天…

#include<cstdio>
#include<cctype>
#include<stack>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; 
}
inline int getint(){
    char ch=gc(); int res=0;
    while(!isdigit(ch)) ch=gc();
    while(isdigit(ch)) res=(res<<3)+(res<<1)+ch-'0', ch=gc();
    return res;
}
const int maxn=40005,maxm=500005,maxe=40005,maxnk=52000005;
int n,_n,m,Q,sz[maxn],a[maxn],v[maxn],dep[maxn],ans,f[maxnk],h[maxnk];
int fir[maxn],nxt[maxe],son[maxe],tot;
bool isleaf[maxn];
void add(int x,int y){
    son[++tot]=y; nxt[tot]=fir[x]; fir[x]=tot; //printf("%d -- %d\n",x,y);
}
int vup[maxn],Tim,df1[maxn],out1[maxn],pos1[maxn],df2[maxn],out2[maxn],pos2[maxn];
void dfs1(int x){
    out1[x]=Tim; sz[x]=1;
    for(int j=fir[x];j;j=nxt[j])
     vup[son[j]]=vup[x]+v[son[j]], dep[son[j]]=dep[x]+1, dfs1(son[j]), sz[x]+=sz[son[j]]; 
    df1[++Tim]=x; pos1[x]=Tim;
}
int c[maxn];
void dfs2(int x){
    out2[x]=Tim;
    stack<int> tmp; for(int j=fir[x];j;j=nxt[j]) tmp.push(son[j]);
    while(!tmp.empty()) dfs2(tmp.top()), tmp.pop();
    df2[++Tim]=x; pos2[x]=Tim;
}
#define f(i,j) f[(i)*(m+1)+(j)]
#define h(i,j) h[(i)*(m+1)+(j)]
int que[maxm],que1[maxm],hd,tl; 
void Dp(int f[],int df[],int out[]){
    for(int i=1;i<=n;i++){
        int x=df[i];
        for(int j=0;j<=m;j++) f(i,j)=f(out[x],j);
        if(a[x]==1){
            for(int j=1;j<=m;j++) f(i,j)=max(f(i,j),f(i-1,j-1)+v[x]);
            continue;
        }
        hd=tl=1; que[hd]=que1[hd]=0;
        for(int j=1;j<=m;j++){
            f(i,j)=max(f(i,j),j*v[x]+que1[hd]);
            while(hd<=tl&&que[hd]<=j-a[x]) hd++;
            int cur=f(i-1,j)-j*v[x];
            while(hd<=tl && que1[tl]<cur) tl--;
            que[++tl]=j; que1[tl]=cur;
        }
    }
}
LL sum_a;
void AllClear(){
    memset(fir,0,sizeof(fir)); tot=0;
    memset(isleaf,1,sizeof(isleaf));
    //memset(f,0,sizeof(f)); memset(h,0,sizeof(h));
    ans=0; sum_a=0;
}
int main(){
    freopen("loj2268.in","r",stdin);
    freopen("loj2268.out","w",stdout);
    Q=getint();
    while(Q--){
        AllClear();
        _n=n=getint(); m=getint(); 
        for(int i=1;i<=_n;i++){ 
            int x=getint(); a[i]=getint(); v[i]=getint(); isleaf[x]=false; sum_a+=a[i];
            if(x) add(x,i); 
            if(a[i]>1) a[++n]=a[i]-1, a[i]=1, v[n]=v[i], add(i,n); 
        }
        Tim=0; dep[1]=1; vup[1]=v[1]; dfs1(1); Tim=0; dfs2(1);
        Dp(f,df1,out1);
        Dp(h,df2,out2);
        for(int i=1;i<=_n;i++) if(isleaf[i]){
            int cnt=min((LL)m,sum_a-dep[i]);
            for(int j=0;j<=cnt;j++) ans=max(ans,f(pos1[i]-1,j)+h(pos2[i]-sz[i],cnt-j)+vup[i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值