1. 程式人生 > >P2607-[ZJOI2008]騎士【基環樹,樹形dp】

P2607-[ZJOI2008]騎士【基環樹,樹形dp】

正題


題目大意

每個騎士有一個不可以同時上場的騎士,和一個戰鬥力。求最大戰鬥力。


解題思路

類似沒有上司的舞會
其實就是在基環樹森林,我們可以利用二次樹形dp的方法。
先找到環,然後強行將環斷開進行一次dp,然後強行連上進行一次dp,兩次答案求最大值。


code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define N 1000010
using namespace std;
struct node{
    ll to,
next; }a[N]; ll w[N],n,x,ans,tot,fa[N],root,f[N],g[N],ls[N],d[N],mark; bool v[N]; void addl(ll x,ll y)//連邊 { a[++tot].to=y; a[tot].next=ls[x]; ls[x]=tot; } void check_c(ll x)//找環 { v[x]=true; if(v[d[x]]) mark=x; else check_c(d[x]); return; } void dp(ll x)//樹形dp { v[x]=true;
f[x]=w[x];g[x]=0; for(ll i=ls[x];i;i=a[i].next) { ll y=a[i].to; if(y!=mark)//如果是斷開就不選 { dp(y); g[x]+=max(f[y],g[y]); f[x]+=g[y]; } else f[y]=-2147483647/3; } return; } int main() { scanf("%lld",&n); for
(ll i=1;i<=n;i++) scanf("%lld%lld",&w[i],&d[i]),addl(d[i],i); for(ll i=1;i<=n;i++) { if(v[i]) continue; check_c(i);dp(mark); ll maxs=max(f[mark],g[mark]); mark=d[mark]; root=0;dp(mark); ans+=max(maxs,max(f[mark],g[mark])); } printf("%lld",ans); }