【洛谷P6847】Magic Tree

题目

题目链接:https://www.luogu.com.cn/problem/P6847
有一棵以 \(1\) 为根,节点从 \(1\)\(n\) 编号的树。
在这棵树上有许多果实,第 \(j\) 个果实会于第 \(d_j\) 天在节点 \(v_j\) 成熟,并且在收获后可获得 \(w_j\) 的果汁。
\(j\) 个果实仅能在第 \(d_j\) 天收获。
收获的方式是断掉这棵树的一条边,这会获得在这条边上作为儿子的那个点的子树上的当天成熟的果实的果汁。
您要求出最多可以获得多少果汁。
\(n,m,k\leq 10^5\)

思路

\(f[x][i]\) 表示以点 \(x\) 为根的子树内,在第 \(i\) 天割掉 \(x\) 与它父亲的连边的最大收益。
枚举 \(x\) 的子树 \(y\),有转移

\[f[x][i]=\max_{j\leq i}(f[y][j])+[d_x=i]\times w_x \]

这是一个 \(\max\) 卷积的形式,我们可以把 \(x\) 的贡献放到最后计算,先用线段树合并来合并 \(x\) 所有子树的收益,最后再加上 \(x\) 的贡献即可。
时间复杂度 \(O(n\log k)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=100010,LG=18;
int n,m,k,tot,head[N],d[N],v[N],rt[N];

struct edge
{
	int next,to;
}e[N];

void add(int from,int to)
{
	e[++tot]=(edge){head[from],to};
	head[from]=tot;
}

struct SegTree
{
	int tot,lc[N*LG*4],rc[N*LG*4];
	ll maxn[N*LG*4],lazy[N*LG*4];
	
	void pushdown(int x)
	{
		if (lazy[x])
		{
			if (lc[x]) maxn[lc[x]]+=lazy[x],lazy[lc[x]]+=lazy[x];
			if (rc[x]) maxn[rc[x]]+=lazy[x],lazy[rc[x]]+=lazy[x];
			lazy[x]=0;
		}
	}
	
	int update(int x,int l,int r,int k,ll v)
	{
		if (!x) x=++tot;
		if (l==r) {	maxn[x]=v; return x; }
		pushdown(x);
		int mid=(l+r)>>1;
		if (k<=mid) lc[x]=update(lc[x],l,mid,k,v);
			else rc[x]=update(rc[x],mid+1,r,k,v);
		maxn[x]=max(maxn[lc[x]],maxn[rc[x]]);
		return x;
	}
	
	ll query(int x,int l,int r,int ql,int qr)
	{
		if (ql<=l && qr>=r) return maxn[x];
		pushdown(x);
		int mid=(l+r)>>1; ll res=0;
		if (ql<=mid) res=max(res,query(lc[x],l,mid,ql,qr));
		if (qr>mid) res=max(res,query(rc[x],mid+1,r,ql,qr));
		return res;
	}
	
	int merge(int x,int y,int l,int r,ll &res1,ll &res2)
	{
		if (!x && !y) return 0;
		pushdown(x); pushdown(y);
		if (!x) { res2=max(res2,maxn[y]); maxn[y]+=res1; lazy[y]+=res1; return y; }
		if (!y) { res1=max(res1,maxn[x]); maxn[x]+=res2; lazy[x]+=res2; return x; }
		if (l==r)
		{
			res1=max(res1,maxn[x]); res2=max(res2,maxn[y]);
			maxn[x]=max(maxn[x]+res2,maxn[y]+res1);
			return x;
		}
		int mid=(l+r)>>1;
		lc[x]=merge(lc[x],lc[y],l,mid,res1,res2);
		rc[x]=merge(rc[x],rc[y],mid+1,r,res1,res2);
		maxn[x]=max(maxn[lc[x]],maxn[rc[x]]);
		return x;
	}
}seg;

void dfs(int x)
{
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to; ll r1=0,r2=0;
		dfs(v);
		rt[x]=seg.merge(rt[x],rt[v],1,m,r1,r2);
	}
	if (d[x])
	{
		ll maxn=seg.query(rt[x],1,m,1,d[x]);
		rt[x]=seg.update(rt[x],1,m,d[x],maxn+v[x]);
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d%d",&n,&k,&m);
	for (int i=2,x;i<=n;i++)
	{
		scanf("%d",&x);
		add(x,i);
	}
	for (int i=1,x;i<=k;i++)
	{
		scanf("%d",&x);
		scanf("%d%d",&d[x],&v[x]);
	}
	dfs(1);
	cout<<seg.maxn[rt[1]];
	return 0;
}
posted @ 2021-07-23 11:45  stoorz  阅读(34)  评论(0编辑  收藏  举报