1. 程式人生 > >[JZOJ5874] 小P的決心

[JZOJ5874] 小P的決心

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); }