hkr04
2019-11-07 21:00:53
题目链接
先感谢题解区的各位!
如果它不是有
现在只多了一条边,那它就是 基环树。相当于多了一个环,其他的结构没有很大的变化。至于对于基环树的处理,一般可以这么处理:
需要注意的是,这里可能不只是一棵树,要对每个块进行相同的处理并加入到答案中。还要注意数据范围问题,需要开 long long
。
对于本题的一般树形态,我们将状态设计为
再来观察一下题目中基环树的形态,由于一个节点只会有一个仇恨的人,所以将我的仇人连向我。这个时候,每个点只连向另一个节点,建出来的一定是一棵 外向树。如下图:
不会出现有两条边指向同一个点的情况:
所以我们在找环的时候,一路回退,一定能找到当前基环树的环。
以上面那棵合法的举个例子,比如我发现
当发现当前点的父亲已被遍历,说明当前点和它的父亲都在这个环上,强行将这条边断开,分别将其作为根节点进行处理。又因为这两个不能同时选,我们干脆就在处理 root 的时候,将它的
在向儿子节点遍历 之前,先设好边界条件,也就是
代码:
#include <cstdio>
typedef long long ll;
const int maxn=1000000+10;
const ll INF=1LL<<60;
int head[maxn],to[maxn],nxt[maxn],fa[maxn];
int tot;
int root;
ll w[maxn],f[maxn][2];
ll ans;
bool vis[maxn];
ll max(ll x,ll y) {return x>y?x:y;}
void add(int u,int v)
{
nxt[++tot]=head[u];
head[u]=tot;
to[tot]=v;
}
void dfs(int u)
{
vis[u]=1;
f[u][0]=0,f[u][1]=w[u];
for (int i=head[u];i;i=nxt[i])
{
int v=to[i];
if (v!=root)
{
dfs(v);
f[u][0]+=max(f[v][0], f[v][1]);
f[u][1]+=f[v][0];
}
else
f[v][1]=-INF;
}
}
void FindCircle(int u)
{
vis[u]=1;
while(!vis[fa[u]])//因为入边唯一,这样一直往前找方便
{
u=fa[u];
vis[u]=1;
}
root=u;
dfs(u);
ll tans=max(f[u][0], f[u][1]);
u=fa[u];
root=u;
dfs(u);
ans+=max(tans, max(f[u][0], f[u][1]));
}
int main()
{
int n;
scanf("%d",&n);
for (int v=1;v<=n;v++)
{
int u;
scanf("%lld%d",&w[v],&u);
add(u, v);
fa[v]=u;
}
for (int i=1;i<=n;i++)
if (!vis[i])
FindCircle(i);
printf("%lld\n",ans);
return 0;
}