P2607-[ZJOI2008]騎士【基環樹,樹形dp】
阿新 • • 發佈:2019-01-05
正題
題目大意
每個騎士有一個不可以同時上場的騎士,和一個戰鬥力。求最大戰鬥力。
解題思路
類似沒有上司的舞會
其實就是在基環樹森林,我們可以利用二次樹形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);
}