[JZOJ5874] 小P的决心

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/82816134

Description

在这里插入图片描述
在这里插入图片描述

Solution

我们发现根据题目中剪枝的定义,如果一个点有兄弟,那么就要么不删这个点,要么将它和它的兄弟全部删掉。

考虑按照DFS序来DP,令F[i]为假设i为最后一个叶子,它的最大答案(DFS序在i后面的不管)

考虑哪些点能转移到F[i],设x为i的第一个有兄弟的祖先,且x不是father[x]的第一个儿子。
那么能转移到F[i]的一定是x的前一个兄弟一直向右下(就是始终走最后一个儿子)直到叶子的这一条链。

那么我们可以用一个栈来维护,每次跑到叶子就将栈清空,回溯的时候将这个点入栈,每当走兄弟时,这个栈直到走到叶子都不会改变了,那么此时统计栈中F[i]的前缀最大(自栈顶向下)以及F[i]-mx[i]的后缀最大之类的,按照最大值在左边还是右边讨论一下就好了。

Code

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
using namespace std;
int a[N],f[N],n,fs[N],nt[2*N],dt[2*N],st[N],fm[N],fx[N],mx[N],pr[N],top,m,ans,id;
void link(int x,int y)
{
	nt[++m]=fs[x];
	dt[fs[x]=m]=y;
}
void dfs(int k,int v)
{
	if(top==0) f[k]=pr[k];
	else
	{
		while(id&&pr[st[id]]<=v) id--;
		f[k]=fm[id]-v;
		if(id) f[k]=max(f[k],fx[id-1]);
		f[k]+=pr[k];
	}
	bool pd=0;
	for(int i=fs[k];i;i=nt[i])
	{
		int p=dt[i];
		if(!pd) pd=1,dfs(p,max(v,pr[k]));
		else 
		{
			id=top;
			fm[0]=fm[top+1]=mx[top+1]=fx[top]=fx[0]=-1e9;
			fod(i,top,1) fm[i]=max(fm[i+1],f[st[i]]),mx[i]=max(mx[i+1],pr[st[i]]);
			mx[0]=mx[1],fm[0]=fm[1];
			fo(i,1,top-1) fx[i]=max(fx[i-1],f[st[i]]-mx[i+1]);
			dfs(p,pr[k]);
		}
	}
	if(!fs[k]) while(top) st[top]=0,top--;
	st[++top]=k;
}
int main()
{
	freopen("temmie.in","r",stdin);
	freopen("temmie.out","w",stdout);
	cin>>n;
	fo(i,1,n) 
	{
		int c,x;
		scanf("%d%d",&pr[i],&c);
		fo(j,1,c) scanf("%d",&x),link(i,x); 
	}
	fo(i,1,n) f[i]=-1e9;
	dfs(1,0);
	fo(i,1,top) ans=max(ans,f[st[i]]);
	printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/hzj1054689699/article/details/82816134
今日推荐