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(n∗k2)
,做法就是选课
其实可以做到
O(n∗k)
,做法也不难,代码比朴素还短,只需要搞出来dfs序,然后在dfs序上dp就好了
为什么是
O(n∗k)
呢?
因为我们只需要遍历一遍状态,然后每一个状态由于是在dfs序上,所以只有两种转移:
1.选择它本身,并跳过它的子树,也就是在dfs序上加上它的子树大小
2.不选它本身,进入它的子树,也就是在dfs序上加1
然后就少了好多冗余状态。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<vector>
#define ll long long
using 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;
}