码迷,mamicode.com
首页 > 其他好文 > 详细

[ZJOI2008] 骑士

时间:2018-08-19 21:54:14      阅读:135      评论:0      收藏:0      [点我收藏+]

标签:tmp   mes   namespace   假设   因此   using   pre   res   const   

题目类型:树形DP(基环树DP)

传送门:>Here<

题意:给出一棵基环树,每个点有点权, 相邻两个点不能都取。求最大和

解题思路

如果给出的不是基环树而是树,那么简单的树形DP,用$dp[u][0/1]$维护即可。

所谓基环树,就是只有一个简单环的树。我们熟知树只要再加一条边那么必定形成环,那么基环树就是$N$个点$N$条边的树(严格来说不能叫树,而是一颗仙人掌)

如果我们依然想利用树形DP来解,那么必须删除一条环内的边。因此我们任意选择一条环内的边,记录其两端。然后当这条边不存在,分别以该边两端作为根节点做树形DP取最大值即可。

假设我们找到了环内的一条边$u,v$,我们把这条边断了,但是题目规定任何一条边连接的两个点不能都选,但是这样做的话有可能$u,v$都被选到。一个很简单的方法就是将点$v$的权值暂时赋为$-∞$,这样以$u$为根节点DP时自然就不会选择它了。由于我们钦定了点$v$不会被选到,所以需要再次考虑能够选到点$v$的方案。因此需要以$v$做为根节点再做一次$DP$

还有一个问题,基环有可能是一条重边,而我们找边$(u,v)$不能简单的记录$u,v$两个点,不然原来的那条边也被我们忽略了。因此我们记录的应当是编号而不是连接的点

Code

/*By DennyQi*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int MAXN = 1000010;
const int MAXM = 2000010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ - && (c < 0 || c > 9)) c = getchar();
    if(c == -) w = -1, c = getchar();
    while(c >= 0 && c <= 9) x = (x << 3) + (x << 1) + c - 0, c = getchar(); return x * w;
}
int N,x,has_root,s,e,EE;
ll ans;
int first[MAXM],nxt[MAXM],to[MAXM],num_edge=-1;
int val[MAXN],vis[MAXN];
ll f[MAXN][2];
inline void add(int u, int v){
    to[++num_edge] = v;
    nxt[num_edge] = first[u];
    first[u] = num_edge;
}
inline void SearchRoot(int u, int father){
    int v;
    vis[u] = 1;
    for(int i = first[u]; i != -1; i = nxt[i]){
        if((v = to[i]) == father) continue;
        if(!vis[v]){
            SearchRoot(v, u);
        }
        else{
            s = u, e = v;
            EE = i;
            continue;
        }
    }
}
void DP(int u, int father){
    int v;
    f[u][0] = 0, f[u][1] = 1LL * val[u];
    for(int i = first[u]; i != -1; i = nxt[i]){
        if((v = to[i]) == father) continue;
        if(i == EE || i == (EE^1)) continue;
        DP(v, u);
        f[u][1] += f[v][0];
        f[u][0] += Max(f[v][0], f[v][1]);
    }
}
int main(){
    N = r;
    memset(first,-1,sizeof(first));
    for(int i = 1; i <= N; ++i){
        val[i] = r;
        x = r;
        add(i, x), add(x, i);
    }
    for(int i = 1; i <= N; ++i){
        if(!vis[i]){
            s = e = 0;
            SearchRoot(i, -1);
            if(!s){
                DP(i, -1);
                ans += Max(f[i][0], f[i][1]);
                continue;
            }
            
            int tmp = val[e];
            val[e] = -INF;
            DP(s, -1);
            ll res = Max(f[s][0], f[s][1]);
            val[e] = tmp;
            
            tmp = val[s];
            val[s] = -INF;
            DP(e, -1);
            val[s] = tmp;
            res = Max(res, Max(f[e][0],f[e][1]));
            ans += res;
        }
    }
    printf("%lld", ans);
    return 0;
} 

[ZJOI2008] 骑士

标签:tmp   mes   namespace   假设   因此   using   pre   res   const   

原文地址:https://www.cnblogs.com/qixingzhi/p/9502544.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!