sdoi2017苹果树

题解:

非常奇妙的一题。。

没有免费操作我都不会$nk$。。。。考试打个暴力就可以走人了

树上有依赖背包问题的正确做法是(为啥我之前学的不是这样的啊)

按照后续遍历做背包

做到一个点的时候 枚举它选不选 不选只能从子树外转移 选的话可以从x-1转移

而不是对每个点求一次$f[i][j]$ 这样是$n*k^2$

前者不管是多重背包还是0/1背包 复杂度都是$nk$的(单调队列优化)

将题目给的条件转化,变成有一条链是免费

我们会发现这样求出的路径是它到根的路径的左边和自己子树的背包

那么我们可以想到 如果按照右左根再遍历一遍 可以得到右边+自己子树的背包

另外有一个性质就是,这条链一定会到叶子

而我们发现对于叶子两个背包合并的话就只多算了当前点并且当前点到根这一段都没有算(只要把一个编号右移一位就没有重复了)

这恰好符合了题目的免费操作

但是注意那些点ai是可以>1也就是说还需要考虑付费部分

一种直观的思路是对其中一种dfs子儿子之前先把$(ai-1,vi)$作为一种物品放进去

其实这等价于再加一个$(ai-1,vi)$的儿子

然后这样就可以卡着空间过了。。 因为多加了儿子,数组需要2.5e7*2*2

***常数巨大但洛谷评测机快就过了

单调队列里面一堆变量写错。。

数组大小一堆开错。。然后查错查了一个小时。。

代码:

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for(int i=h;i<=t;i++)
#define dep(i,t,h) for(int i=t;i>=h;i--)
#define ll long long
#define me(x) memset(x,0,sizeof(x))
#define mep(x,y) memcpy(x,y,sizeof(y))
#define mid (t<=0?(h+t-1)/2:(h+t)/2)
namespace IO{
    char ss[1<<24],*A=ss,*B=ss;
    IL char gc()
    {
        return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;
    }
    template<class T> void read(T &x)
    {
        rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48);
        while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; 
    }
    char sr[1<<24],z[20]; int Z,C1=-1;
    template<class T>void wer(T x)
    {
        if (x<0) sr[++C1]='-',x=-x;
        while (z[++Z]=x%10+48,x/=10);
        while (sr[++C1]=z[Z],--Z);
    }
    IL void wer1()
    {
        sr[++C1]=' ';
    }
    IL void wer2()
    {
        sr[++C1]='
';
    }
    template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;}
    template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} 
    template<class T>IL T MAX(T x,T y){return x>y?x:y;}
    template<class T>IL T MIN(T x,T y){return x<y?x:y;}
};
using namespace IO;
const int N=6e5;
const int N2=51000000;
int n,k,ans[N],a[N],v[N],dp1[N2],dp2[N2],sum[N],cnt;
int dfn1[N],dfn2[N];
vector<int> ve[N];
struct re{
    int a,b;
}p[N];
void dfs1(int x)
{
    sum[x]=1;
    for (int i=0;i<ve[x].size();i++)
    {
        ans[ve[x][i]]=ans[x]+v[x];
        dfs1(ve[x][i]);
        sum[x]+=sum[ve[x][i]];
    }
    dfn1[x]=++cnt;
    int n1=dfn1[x]-1,k1=n1*(k+1);
    int k2=dfn1[x]*(k+1);
    int h=1,t=0; p[1]=(re){0,0};
    rep(i,0,k)
    {
        if (h<=t&&(i-p[h].a)>a[x]) h++;
        if (h<=t) dp1[k2+i]=v[x]*i+p[h].b;
        int now=dp1[k1+i]-v[x]*i;
        while (h<=t&&now>=p[h].b) t--;
        p[++t]=(re){i,now};
    }
    n1=dfn1[x]-sum[x],k1=n1*(k+1);
    rep(i,0,k)
    { 
      dp1[k2+i]=MAX(dp1[k2+i],dp1[k1+i]);
      if (i) maxa(dp1[k2+i],dp1[k2+i-1]);
    }
}
bool vis[N];
void dfs2(int x)
{
    vis[x]=1;
    sum[x]=1;
    for (int i=(int)(ve[x].size())-1;i>=0;i--)
    {
        dfs2(ve[x][i]);
        sum[x]+=sum[ve[x][i]];
    }
    dfn2[x]=++cnt;
    int n1=dfn2[x]-1,k1=n1*(k+1);
    int k2=dfn2[x]*(k+1);
    int h=1,t=0; p[1]=(re){0,0};
    rep(i,0,k)
    {
        if (h<=t&&(i-p[h].a)>a[x]) h++;
        if (h<=t) dp2[k2+i]=v[x]*i+p[h].b;
        int now=dp2[k1+i]-v[x]*i;
        while (h<=t&&now>=p[h].b) t--;
        p[++t]=(re){i,now};
    }
    n1=dfn2[x]-sum[x],k1=n1*(k+1);
    rep(i,0,k)
    { 
      dp2[k2+i]=MAX(dp2[k2+i],dp2[k1+i]);
      if (i) maxa(dp2[k2+i],dp2[k2+i-1]);
    }
}
int main()
{
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    int T;
    read(T);
    rep(ttt,1,T)
    {
        read(n); read(k); me(dp1); me(dp2);
        rep(i,0,N-1)
        {
            vector<int> v2;
            v2.swap(ve[i]);
        }
        rep(i,1,n) 
        {
          int x; read(x);
          read(a[i]); read(v[i]);
          if (x) ve[x].push_back(i);
          a[i+n]=a[i]-1,v[i+n]=v[i];
          a[i]=1;
          ve[i].push_back(i+n);
        }
        cnt=0; dfs1(1);
        cnt=0; dfs2(1);
        int num=0;
        rep(i,n+1,2*n)
          rep(j,0,k) 
          maxa(num,ans[i]+dp1[dfn1[i]*(k+1)+j]
          +dp2[(dfn2[i]-1)*(k+1)+k-j]);
        cout<<num<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/yinwuxiao/p/10057559.html