1. 程式人生 > >BZOJ1040-[ZJOI2008]騎士

BZOJ1040-[ZJOI2008]騎士


題解:
題意:有一個基環樹森林,每個點有權值,取出互不相鄰的任意個點,問最大權值和是多少。
先考慮是一棵樹的情況:
dp[i][0/1]表示以i為根的子樹中i(1)或不取(0)的最大權值和
dp[u][0]=max(dp[v][0],dp[v][1])
dp[u][1]=a[u]+dp[v][0]
那加一條邊變成了環之後呢?
取出環上相鄰的兩個點xy,將連邊斷開,變成一棵樹,而這兩個點不能同時取。
分別以這兩個點為根做樹形dp

ans=max(dp1[x][0],dp2[y][0])
Code

#include<bits/stdc++.h>
#define N 2000005
#define ll long long
using namespace std;
int tot=-1,rr,rl,head[N],s[N],n,vis[N],flag;
ll f[N][2],ans,a[N];
struct node
{
    int next,vet;
}edge[N];
void add(int u,int v)
{
    edge[++tot].vet=v;
    edge[tot].next=head[u];
    head[u]=tot;
}
void
dfs(int u,int fa) { vis[u]=true; for(int i=head[u];i!=-1&&(!flag);i=edge[i].next) { int v=edge[i].vet; if(v!=fa) { if(vis[v]) { rl=u; rr=v; s[i]=s[i^1]=-1; flag=true; break
; } dfs(v,u); } } } void dp(int u,int fa,int ban) { vis[u]=true; if(u!=ban)f[u][1]=a[u];else f[u][1]=0; f[u][0]=0; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].vet; if(v!=fa&&s[i]!=-1) { dp(v,u,ban); f[u][0]+=max(f[v][0],f[v][1]); f[u][1]+=f[v][0]; } } } int main() { memset(head,-1,sizeof(head)); scanf("%d",&n); for(int i=1;i<=n;i++) { int x; scanf("%lld%d",&a[i],&x); add(x,i);add(i,x); } for(int i=1;i<=n;i++) if(!vis[i]) { flag=false; dfs(i,0); ll mx=0; dp(rl,0,rr); mx=max(f[rl][0],f[rl][1]); dp(rr,0,rl); mx=max(mx,max(f[rr][0],f[rr][1])); ans+=mx; } printf("%lld\n",ans); }