zoukankan      html  css  js  c++  java
  • BZOJ 1040: [ZJOI2008]骑士(基环树dp)

     http://www.lydsy.com/JudgeOnline/problem.php?id=1040

    题意:

    思路:

    这是基环树,因为每个人只会有一个厌恶的人,所以每个节点只会有一个父亲节点,但是根节点也是有父亲节点的,所以在树中肯定是存在一个环的,只要删除该环中的任意一条边,那么就能将该图变成一颗树。

    如果是树的话,那就很简单了,d[u][0/1] dp求解即可。

    现在假设删除的边是e,两端的节点分别是u,v,首先对u为根的树作一次dp,最后取d[u][0](v取不取都无所谓),不能取d[u][1](因为此时可能也取了v)。但是这样的话没有考虑选u的情况,所以再对v为根的树作一次dp,最后取d[v][0]。两者取大者即可。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<map>
    using namespace std;
    const int maxn = 1000000+5;
    typedef long long ll;
    
    int n,tot=0,edgeID,edgeLeft,edgeRight;
    int head[maxn],vis[maxn];
    ll val[maxn], d[maxn][2];
    
    struct node
    {
        int v,next;
    }e[2*maxn];
    
    void addEdge(int u,int v)
    {
        e[tot].v = v;
        e[tot].next = head[u];
        head[u] = tot++;
    }
    
    void dfs(int u, int fa)
    {
        vis[u] = 1;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v = e[i].v;
            if(v == fa)  continue;
            if(!vis[v])  dfs(v,u);
            else   //找到了环
            {
                edgeID = i;  //记录边和两端顶点
                edgeLeft = u;
                edgeRight = v;
            }
        }
    }
    
    ll dp(int u, int fa)
    {
        d[u][0] = 0, d[u][1] = val[u];
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v = e[i].v;
            if(v==fa)  continue;
            if(i==edgeID || i==(edgeID^1))  continue;  //正向边和反向边
            dp(v,u);
            d[u][0] += max(d[v][0],d[v][1]);
            d[u][1] += d[v][0];
        }
        return d[u][0];
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        memset(head,-1,sizeof(head));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%lld%d",&val[i],&x);
            addEdge(i,x);
            addEdge(x,i);
        }
        ll ans = 0;
        for(int i=1;i<=n;i++)
        {
            if(vis[i])  continue;
            dfs(i,-1);
            ans += max(dp(edgeLeft,-1),dp(edgeRight,-1));
        }
        printf("%lld
    ",ans);
        return 0;
    }
    

      

  • 相关阅读:
    Codeforces Round #670 (Div. 2)
    BUPT训练随笔(round 5)
    BUPT训练随笔(round 4)
    BUPT训练随笔(round 3)
    BUPT训练随笔(round 2)
    BUPT训练随笔(round 1)
    ctsc&apio2018八日游
    没有标题0.0
    splay:优雅的区间暴力!
    FJOI游记(日记向 不定期更新)
  • 原文地址:https://www.cnblogs.com/zyb993963526/p/8047818.html
Copyright © 2011-2022 走看看