[ZJOI2008] 骑士

Posted qixingzhi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2008] 骑士相关的知识,希望对你有一定的参考价值。

题目类型:树形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] 骑士的主要内容,如果未能解决你的问题,请参考以下文章

[ZJOI2008]骑士

BZOJ 1040: [ZJOI2008]骑士

bzoj1040[ZJOI2008]骑士

BZOJ1040: [ZJOI2008]骑士

ZJOI2008骑士[树形dp]

bzoj 1040 1040: [ZJOI2008]骑士