【NOIP2017提高A组集训10.25】摘Galo (树形dp)

来源:互联网 发布:爱普生tx800清零软件 编辑:程序博客网 时间:2024/05/22 06:30

Description

0v0在野外看到了一棵Galo树,看到食物的0v0瞪大了眼睛,变成了OvO。
这棵Galo树可以看做是一棵以1号点为根的n个点的有根数,除了根节点以外,每个节点i都有一个Galo,美味度为w[i]。
OvO发现,如果她摘下了i号Galo,那么i的子树中的Galo以及i到根的路径上的其他Galo都会死掉。
OvO的袋子只能装k个Galo,她的嘴巴里还能叼1个,请问她所摘Galo的美味度之和的最大值是多少?

Input

第一行两个正整数n,k。
第二行到第n行,第i行两个正整数f[i],w[i],表示i号点的父亲为f[i] (保证x[i]

Output

一行一个非负整数,为最大美味值。

Sample Input

4 1
1 10
2 3
2 6

Sample Output

10

Data Constraint

30% n,k<=200
30% n*k*k<=10^7
40% n*k<=10^7
对于所有数据,n,k,w[i]<=10^5

Hint

尽管OvO最多可以摘两个Galo,但是最优情况是只摘下第二个点的Galo,美味度为10。


题解

这题朴素的dp复杂度是O(nk2),做法就是选课
其实可以做到O(nk),做法也不难,代码比朴素还短,只需要搞出来dfs序,然后在dfs序上dp就好了
为什么是O(nk)呢?
因为我们只需要遍历一遍状态,然后每一个状态由于是在dfs序上,所以只有两种转移:
1.选择它本身,并跳过它的子树,也就是在dfs序上加上它的子树大小
2.不选它本身,进入它的子树,也就是在dfs序上加1
然后就少了好多冗余状态。
代码:

#include<cstdio>#include<cstring>#include<iostream>#include<cmath>#include<algorithm>#include<cstdlib>#include<vector>#define ll long longusing namespace std;inline int read(){    int x=0;char ch=' ';int f=1;    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();    if(ch=='-')f=-1,ch=getchar();    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();    return x*f;}struct edge{    int to,next;}e[200001];int n,k,tot;int head[100001];inline void addedge(int x,int y){    e[++tot].to=y;e[tot].next=head[x];head[x]=tot;}vector<ll> f[100005];ll w[100005];int size[100005];int dfn[100005],dfn_clock;inline void dfs(int x,int fa){    size[x]=1;    dfn[++dfn_clock]=x;    for(int i=head[x];i;i=e[i].next){        int u=e[i].to;        if(u==fa)continue;        dfs(u,x);        size[x]+=size[u];    }}inline ll dp(int i,int j){    if(i>n)return 0;    if(j==0)return 0;    if(f[i][j])return f[i][j];    f[i][j]=max(f[i][j],dp(i+size[dfn[i]],j-1)+w[dfn[i]]);    f[i][j]=max(f[i][j],dp(i+1,j));    return f[i][j];}int main(){    freopen("galo.in","r",stdin);    freopen("galo.out","w",stdout);    n=read();k=read()+1;    for(int i=2;i<=n;i++){        int fa=read();        w[i]=read();        addedge(fa,i);addedge(i,fa);    }    dfs(1,0);    for(int i=1;i<=n;i++){        f[i].resize(k+1);    }    printf("%lld",dp(1,k));    return 0;}
阅读全文
0 0