【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=1040
【题意】
给一个基环森林,每个点有一个权值,求一个点集使得点集中的点无边相连且权值和最大。
【思路】
注意题目中的有向边其实就是无向边。然后有多个联通块,每个联通块中有且仅有一个环。
如果没有环的话可以用树形DP,解决这个问题。
设f[i][0],f[i][1]分别表示以i为根,不选/选i时的最大权值。则有转移式:
f[i][0]=sigma{ max(f[son(i)][0],f[son(i)][1]) }
f[i][1]=sigma{ f[son(i)][0] }
对于一个环,我们任选一条边拆开,然后以边的两点U,V为根做树形DP,再考虑边UV存在,有两种情况:
1) 强制不选U,V任意,环的贡献为以U做DP的f[U][0]
2) 强制不选V,U任意,环的贡献为以V做DP的f[V][0]
【科普】
基环外向树就是一棵树加一条边(好厉害的名字<_<
【代码】
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 5 using namespace std; 6 7 typedef long long ll; 8 const int N = 1e6+10; 9 10 struct Edge { 11 int v,nxt; 12 }e[N<<1]; 13 int en=1,front[N]; 14 void adde(int u,int v) 15 { 16 en++; e[en].v=v,e[en].nxt=front[u],front[u]=en; 17 } 18 19 int n,w[N],vis[N]; 20 ll f[N][2]; 21 22 ll read() 23 { 24 char c=getchar(); ll f=1,x=0; 25 while(!isdigit(c)) {if(c=='-') f=-1; c=getchar(); }; 26 while(isdigit(c)) x=x*10+c-'0',c=getchar(); 27 return x*f; 28 } 29 30 int U,V,E; 31 void dfs(int u,int fa) 32 { 33 vis[u]=1; 34 for(int i=front[u];i;i=e[i].nxt) { 35 if((i^1)==fa) continue; 36 int v=e[i].v; 37 if(vis[v]) { 38 U=u; V=v; E=i; 39 continue; 40 } 41 dfs(v,i); 42 } 43 } 44 void treedp(int u,int fa,int ban) 45 { 46 f[u][1]=w[u],f[u][0]=0; 47 for(int i=front[u];i;i=e[i].nxt) { 48 if((i^1)==fa) continue; 49 if(i==ban||(i^1)==ban) continue; 50 int v=e[i].v; 51 treedp(v,i,ban); 52 f[u][0]+=max(f[v][1],f[v][0]); 53 f[u][1]+=f[v][0]; 54 } 55 } 56 57 int main() 58 { 59 n=read(); 60 int v; 61 FOR(i,1,n) { 62 w[i]=read(),v=read(); 63 adde(i,v),adde(v,i); 64 } 65 ll ans=0; 66 FOR(i,1,n) if(!vis[i]) { 67 dfs(i,-1); 68 treedp(U,-1,E); 69 ll tmp=f[U][0]; 70 treedp(V,-1,E); 71 tmp=max(tmp,f[V][0]); 72 ans+=tmp; 73 } 74 printf("%lld",ans); 75 return 0; 76 }