【题意分析】
给你一个带权基环树森林,求它的点集的无邻点子集的最大权值和。
【解题思路】
对于树的部分,做一遍拓扑排序+递推即可(f[i][j]表示第i个节点选取状态为j(0/1)可以得到的最大权值和)。时间复杂度O(n)。
对于环的部分,做一遍DP。g[i][j]表示环上第i个节点选取状态为j时,编号从1到i的节点的最大子树权值和。转移方程:
•g[i][0]=max(g[i-1][0],g[i-1][1])+f[i][0];
•g[i][1]=g[i-1][0]+f[i][1];
但因为这是个环,所以要枚举环上第一个元素取不取。若取,则答案为g[n][0],否则答案为max(g[n][0],g[n][1])。时间复杂度O(n)。
总时间复杂度O(n)。
【参考代码】
1 #include <bits/stdc++.h> 2 #define range(i,low,high) for(register int i=(low);i<(high);++i) 3 #define dange(i,high,low) for(register int i=(high);i>(low);--i) 4 #define __function__(type) __attribute__((optimize("-O2"))) inline type 5 #define __procedure__ __attribute__((optimize("-O2"))) inline void 6 using namespace std; 7 8 static int n; bool vis[1000005]={0}; 9 int oue[1000005],ind[1000005],que[1000005],loop[1000005]; 10 long long v[1000005],f[1000005][2]={0},g[1000005][2]; 11 12 int main() 13 { 14 scanf("%d",&n); 15 range(i,1,n+1) scanf("%lld%d",v+i,oue+i),++ind[oue[i]]; 16 int tail=0; long long ans=0; 17 range(i,1,n+1) {f[i][1]=v[i]; if(!ind[i]) que[tail++]=i;} 18 range(head,0,tail) 19 { 20 int fr=que[head],to=oue[fr]; vis[to]=1; 21 f[to][0]+=max(f[fr][0],f[fr][1]),f[to][1]+=f[fr][0]; 22 if(!--ind[to]) que[tail++]=to; 23 } 24 range(i,1,n+1) if(ind[i]>0) 25 { 26 int cnt=1; --ind[loop[1]=i]; 27 for(int j=oue[i];j!=i;j=oue[j]) --ind[loop[++cnt]=j]; 28 g[1][0]=f[loop[1]][0],g[1][1]=0; 29 range(j,2,cnt+1) 30 { 31 int k=loop[j]; 32 g[j][0]=max(g[j-1][0],g[j-1][1])+f[k][0]; 33 g[j][1]=g[j-1][0]+f[k][1]; 34 } 35 long long tmp=max(g[cnt][0],g[cnt][1]); 36 g[1][0]=0,g[1][1]=f[loop[1]][1]; 37 range(j,2,cnt+1) 38 { 39 int k=loop[j]; 40 g[j][0]=max(g[j-1][0],g[j-1][1])+f[k][0]; 41 g[j][1]=g[j-1][0]+f[k][1]; 42 } 43 ans+=max(tmp,g[cnt][0]); 44 } 45 return printf("%lld ",ans),0; 46 }