我们发现根据题目中剪枝的定义,如果一个点有兄弟,那么就要么不删这个点,要么将它和它的兄弟全部删掉。
考虑按照DFS序来DP,令F[i]为假设i为最后一个叶子,它的最大答案(DFS序在i后面的不管)
考虑哪些点能转移到F[i],设x为i的第一个有兄弟的祖先,且x不是father[x]的第一个儿子。
那么能转移到F[i]的一定是x的前一个兄弟一直向右下(就是始终走最后一个儿子)直到叶子的这一条链。
那么我们可以用一个栈来维护,每次跑到叶子就将栈清空,回溯的时候将这个点入栈,每当走兄弟时,这个栈直到走到叶子都不会改变了,那么此时统计栈中F[i]的前缀最大(自栈顶向下)以及F[i]-mx[i]的后缀最大之类的,按照最大值在左边还是右边讨论一下就好了。
#include
#include
#include
#include
#include
#include
#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);
}