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