[JZOJ5874] 小P的決心
阿新 • • 發佈:2018-11-10
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);
}