bzoj1040[ZJOI2008]骑士——基环树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1040

就是基环树的裸题;

每个骑士有一个憎恨的骑士,也就是n个点n条边,能看出是基环树;

基环树的套路就是把环断开,然后强制不选根节点,树形DP取较大的作为答案。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int const maxn=1e6+5;
int n,m,v[maxn],head[maxn],ct,va[maxn],vb[maxn],fa[maxn];
ll ans,x0[maxn],x1[maxn];
struct N{
    int to,next;
    N(int t=0,int n=0):to(t),next(n) {}
}edge[maxn<<1];
void add(int x,int y){edge[++ct]=N(y,head[x]);head[x]=ct;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void dfs(int x,int f)
{
    x0[x]=0;x1[x]=v[x];
    for(int i=head[x],u;i;i=edge[i].next)
    {
        u=edge[i].to;
        if(u==f)continue;
        dfs(u,x);
        x0[x]+=max(x0[u],x1[u]);
        x1[x]+=x0[u];
    }
    return;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1,a,b;i<=n;i++)
    {
        scanf("%d%d",&v[i],&b);
        if(find(i)!=find(b))
        {
            fa[find(i)]=find(b);
            add(i,b);add(b,i);
        }
        else va[++m]=i,vb[m]=b;//形成环 
    }
    for(int i=1;i<=m;i++)//每棵基环树 
    {
        ll t;
        dfs(va[i],0);t=x0[va[i]];
        dfs(vb[i],0);t=max(t,x0[vb[i]]);
        ans+=t;
    }
    printf("%lld",ans);
    return 0;
}

 

posted @ 2018-06-04 19:39  Zinn  阅读(108)  评论(0编辑  收藏  举报