其他分享
首页 > 其他分享> > [SDOI2017]苹果树

[SDOI2017]苹果树

作者:互联网

此题爆肝一个晚上的菜鸡,呜呜呜

题意:luogu P3780

一棵树,每个点都有全值和取它次数的上限。h为你所取的最大深度(根为1),c为你取的个数,满足c-h<=k(k题目给出)

思路:

相当于免费取一条链(尽量长肯定到叶子),然后我么可以想到枚举这条链。
除了链上的点之外的其它点都要满足父子关系。
我一开始想到链上随便选于是枚举链上不免费选的个数,然后后贪心从大到小选,其余的点跑多重背包dp。
当然这样是有问题的,链上的点会作为父亲影响到非链上的点。

ps.树上背包复杂度是\(O(n^2)\),树上多重背包后序遍历dfs序转化后+单调队列优化也是可以\(O(n^2)\),为此我还去学了一下,当然这里是\(O(n*k)\)。

为了满足父子关系又方便搞免费链,每个点拆成2个容量分别为\(1\)和\(a_i-1\)的点,即把\(a_i-1\)挂在\(1\)的点上
问题轻松转化为:

code

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int K=5e5+5;
const int NK=60000005;
typedef long long ll;
struct node {
	int x,data;
}Q[K];
int num,n,k,sz[N],p[N],tim,dfn1[N],dfn2[N],dis[N],dp[NK][2],fa[N],a[N],val[N],Out[N];
vector<int> V[N];
inline int Id(int u,int v) {return u*(k+1)+v;}
void dfs1(int u) {
	sz[u]=1;
	for(int i=0;i<V[u].size();i++) {
		int v=V[u][i];
		dis[v]=dis[u]+val[v];
		dfs1(v);
		sz[u]+=sz[v]; 
//		printf("%d %d\n",v,dis[v]);
	}
	dfn1[u]=++tim; p[tim]=u;
}
void dfs2(int u) {
	for(int i=V[u].size()-1;i>=0;i--) {
		int v=V[u][i];
		dfs2(v);
	}
	dfn2[u]=++tim;p[tim]=u;
}
void DP(bool f) {
//	for(int i=1;i<=num;i++) printf("%d ",p[i]);
//	puts("");
	for(int i=1;i<=num;i++) {
		int h=1,t=0,u=p[i];
		Q[++t]=(node){0,0};
		for(int j=1;j<=k;j++) {
			while(h<=t&&Q[h].x+a[u]<j) h++;
			if(h<=t)dp[Id(i,j)][f]=max(dp[Id(i-sz[u],j)][f],Q[h].data+j*val[u]);
//			printf("(%d,%d): %d\n",i,j,dp[Id(i,j)][f]);
			node nw=(node){j,dp[Id(i-1,j)][f]-j*val[u]};
			while(h<=t&&Q[t].data<=nw.data) t--;
			Q[++t]=nw;
		}
	}
//	puts("");
}
int main() {
	int Q;
	scanf("%d",&Q);
	while(Q--) {
		int ans=0;
		scanf("%d%d",&n,&k);
		num=n;
		for(int i=1;i<=n;i++) {
			scanf("%d%d%d",&fa[i],&a[i],&val[i]);
			if(fa[i])V[fa[i]].push_back(i);
		}
		for(int i=1;i<=n;i++) {
			Out[fa[i]]++;
			if(a[i]>1) {
				++num;V[i].push_back(num); a[num]=a[i]-1;a[i]=1;val[num]=val[i];
			}
		}
//		printf("!\n");
		tim=0;dis[1]=val[1];dfs1(1);DP(0);
//		printf("!\n");
		tim=0;dfs2(1);DP(1);
		for(int i=1;i<=n;i++) {
			if(Out[i])continue;
			for(int j=0;j<=k;j++) {
//				printf("%d %d %d %d %d\n",i,j,dis[i],dp[Id(dfn1[i]-1,j)][0],dp[Id(dfn2[i]-2,k-j)][1]);
				ans=max(ans,dis[i]+dp[Id(dfn1[i]-1,j)][0]+dp[Id(dfn2[i]-sz[i],k-j)][1]);
			}
		}
		printf("%d\n",ans);
		for(int i=1;i<=n;i++) V[i].clear(),Out[i]=0;
		memset(dp,0,sizeof(dp));
	}
	return 0;
}

标签:val,int,SDOI2017,tim,链上,num,苹果树,dp
来源: https://www.cnblogs.com/bestime/p/15573976.html