https://loj.ac/problem/10162
题目描述
每个骑士都有一个战斗力和一个厌恶的人,他不会和厌恶的人一起出征,求最多能达到的最大战斗力。
思路
由于题目中每个点必定会连出一条边,所以对于每个连通块内,必定是一棵基环树,对于基环树,我们很难进行(dp),一个简单的方法是我们先求出那个简单环,随后断开环上的任意一条边,以这条边连接的两个点为根做一次树形(dp),并保证根节点不选即可。
对于在基环树上找环,我们可以考虑用(dfs)序做,当我们访问到一条边从(dfs)序小的访问到已确定(dfs)序大的节点时,这两个点必定是环上的两点,不断往回跳记录答案即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
ll read()
{
ll res=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
return res*w;
}
void write(ll x)
{
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
int head[N],tot=1,to[N<<1],nxt[N<<1];
void add_edge(int x,int y)
{
nxt[++tot]=head[x];
head[x]=tot;
to[tot]=y;
}
int dfn[N],idx,fa[N],loop[N],cnt;
void get_loop(int u)
{
dfn[u]=++idx;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa[u])continue ;
if(dfn[v])
{
if(dfn[v]<dfn[u])continue ;
loop[++cnt]=v;
for(;v!=u;v=fa[v])
loop[++cnt]=fa[v];
}
else
{
fa[v]=u;
get_loop(v);
}
}
}
ll a[N],f[N][2],st;
void dfs(int u,int fa)
{
// cout<<u<<':'<<endl;
f[u][1]=a[u];
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa||i==(st^1)||i==st)continue ;
// cout<<i<<' '<<v<<endl;
dfs(v,u);
f[u][0]+=max(f[v][0],f[v][1]);
f[u][1]+=f[v][0];
}
}
int main()
{
int n=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
int x=read();
add_edge(i,x);add_edge(x,i);
}
ll ans=0;
for(int i=1;i<=n;i++)
if(!dfn[i])
{
cnt=0;
get_loop(i);
// for(int j=1;j<=cnt;j++)
// cout<<loop[j]<<' ';
// cout<<endl;
for(int i=head[loop[1]];i;i=nxt[i])
if(to[i]==loop[cnt])st=i;
// cout<<st<<' '<<(st^1)<<endl;
dfs(loop[1],0);
ll x=f[loop[1]][0];
for(int i=head[loop[cnt]];i;i=nxt[i])
if(to[i]==loop[1])st=i;
memset(f,0,sizeof(f));
dfs(loop[cnt],0);
ans+=max(x,f[loop[cnt]][0]);
// cout<<x<<' '<<f[loop[cnt]][0]<<endl;
}
write(ans);
}