【树形DP】BZOJ1040-[ZJOI2008]骑士

时间:2023-03-10 02:52:55
【树形DP】BZOJ1040-[ZJOI2008]骑士

【题目大意】

有n个骑士,给出他们的能力值和最痛恨的一位骑士。选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力,求战斗力的最大值。

【思路】

首先yy一下,可以知道这是一个基环森林。我们可以用以下方法:

首先在每一棵基环树的环上任意找到一条边(用dfs来实现),记它的两个端点为u和v。然后删掉这条边(我这里用的方法是记录u,v在对方容器中的位置,并在后续操作中忽略这条边)。由于u和v不能同时取,在删掉u和v之间的边后存在以下两种情况:

令g[i]为不取i时i及其子树的最大战斗力总和,f[i]则表示取i。

(1)u不取,v任意。则以u为根进行树形DP,结果为g[u];

(2)v不取,u任意。则以v为根进行树形DP,结果为g[v]。

然后ans+max(g[u],g[v])。

树形DP的过程非常地常规,就不赘述了 。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN=+;
typedef long long ll;
vector<int> E[MAXN];
int n,power[MAXN],hate[MAXN];
int vis[MAXN];
int U,V;
ll g[MAXN],f[MAXN]; void addedge(int u,int v)
{
E[u].push_back(v);
E[v].push_back(u);
} void dfs(int u,int fr)
{
vis[u]=;
for (int i=;i<E[u].size();i++)
{
int to=E[u][i];
if (to!=fr)
{
if (!vis[to]) dfs(to,u);
else
{
vis[to]=;
U=u;V=to;
return;
}
}
}
} void TreeDP(int u,int fr,int rt,int ban)
{
vis[u]=;
f[u]=power[u];
g[u]=;
for (int i=;i<E[u].size();i++)
{
int to=E[u][i];
if (u==rt && i==ban) continue;
if (to!=fr && to!=rt)
{
TreeDP(to,u,rt,ban);
f[u]+=g[to];
g[u]+=max(g[to],f[to]);
}
}
} void init()
{
scanf("%d",&n);
for (int i=;i<=n;i++)
{
scanf("%d%d",&power[i],&hate[i]);
addedge(i,hate[i]);
}
} void get_ans()
{
memset(vis,,sizeof(vis));
ll ans=;
for (int i=;i<=n;i++)
if (!vis[i])
{
dfs(i,-);
int banu,banv;
for (int i=;i<E[U].size();i++) if (E[U][i]==V)
{
banu=i;
break;
}
for (int i=;i<E[V].size();i++) if (E[V][i]==U)
{
banv=i;
break;
} TreeDP(U,-,U,banu);
ll uans=g[U]; TreeDP(V,-,V,banv);
ll vans=g[V];
ans+=max(uans,vans);
}
cout<<ans<<endl;
} int main()
{
init();
get_ans();
return ;
}