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

BZOJ1040[ZJOI2008]騎士

stat 步驟 rep max read sed src == mat

技術分享
 1 #include <cmath>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <algorithm>
 6 using namespace std;
 7 
 8 typedef long long ll;
 9 const int N=1000005,M=2000005;
10 
11 int n,tot,U,V,E,now[N],prep[M],son[M],val[N];
12 ll ans,f[N],g[N];
13 bool vis[N];
14 15 int read(){ 16 static int x,f; static char ch; x=0,f=1; 17 for (ch=getchar();!isdigit(ch);ch=getchar()) if (ch==-) f=-1; 18 for (;isdigit(ch);ch=getchar()) x=x*10+ch-0; return x*f; 19 } 20 21 void link(int x,int y){prep[++tot]=now[x],now[x]=tot,son[tot]=y;} 22 23 void dfs(int x,int
y){ 24 vis[x]=1; 25 for (int i=now[x],so=son[i];i!=-1;i=prep[i],so=son[i]){ 26 if (so!=y){ 27 if (vis[so]){ 28 U=x,V=so,E=i; 29 continue; 30 }else dfs(so,x); 31 } 32 } 33 } 34 35 void tree_dp(int x,int y,int ban){ 36
f[x]=val[x],g[x]=0; 37 for (int i=now[x],so=son[i];i!=-1;i=prep[i],so=son[i]){ 38 if (so!=y&&i!=ban&&(i^1)!=ban){ 39 tree_dp(so,x,ban); 40 f[x]+=g[so],g[x]+=max(f[so],g[so]); 41 } 42 } 43 } 44 45 int main(){ 46 n=read(); tot=-1; 47 memset(now,-1,sizeof(now)); 48 for (int x,i=1;i<=n;i++){ 49 val[i]=read(),x=read(); 50 link(x,i),link(i,x); 51 } 52 ans=0; 53 for (int i=1;i<=n;i++){ 54 if (!vis[i]){ 55 dfs(i,0); 56 tree_dp(U,0,E); 57 ll x=g[U]; 58 tree_dp(V,0,E); 59 ans+=max(x,g[V]); 60 } 61 } 62 printf("%lld\n",ans); 63 return 0; 64 }
View Code

題目大意:給定一個基環外向森林,點帶權,求最大點獨立集。n<=10^6.

做法:基環樹dp,設(u,v)為環上的一條邊,f[x]表示以x為根的子樹中選x的最大權和,g[x]不選。

考慮如下步驟:

1:將這條邊斷開,變為樹。

2:u,v必有一個點不選,考慮分別以u,v為根做一次dp,然後用max(g[u],g[v])更新答案。

細節:(u,v)怎麽找? dfs一次,如果x的出邊son之前訪問過了,那麽(x,son)就是換上的一條邊,記錄一下即可。

BZOJ1040[ZJOI2008]騎士