题面
https://www.luogu.org/problemnew/show/P4410
题意:求仙人掌上最大独立集。
- $orzyyb$ $tarjan$中顺便$dp$的思路是在是太妙了。
- 同时用两个数组处理问题,减少思维复杂度(我jo的我写的代码总有一种暴力美学)。
- $g[x][0/1]$ 表示$x$及$x$的子树中,如果x选,最大独立集是多少。
- $f[x][0/1][0/1]$ 有意义当且仅当$x$在环上,并且不为环的头。 设$x$为环的头,$t$为环的尾,$f[y][0/1][0/1]$ 表示$y$ 选或不选,$t$选或不选,$y$及$y$的子树里的最大独立集是多少。
- 然后转移就可以了,注意要在$y$中扣除先前转移$t$的影响。
// luogu-judger-enable-o2 #include<cstdio> #include<cstring> #include<iostream> #include<stack> #include<vector> #define INF 1000000007 #define ri register int #define N 100500 using namespace std; int n,tn,m; int cir[N]; int a[N]; int ans; inline int read() { int ret=0,f=0; char ch=getchar(); while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0'&&ch<='9') ret*=10,ret+=ch-'0',ch=getchar(); return f?-ret:ret; } struct graph { vector<int> to[N]; int dfn[N],low[N],cnt; stack<int> s; int f[N][2][2],g[N][2]; // state:t state:x void add_edge(int a,int b) { to[a].push_back(b); to[b].push_back(a); } void tarjan(int x) { dfn[x]=low[x]=++cnt; s.push(x); g[x][1]=a[x]; g[x][0]=0; for (ri i=0;i<to[x].size();i++) { int y=to[x][i]; if (dfn[y]) low[x]=min(low[x],dfn[y]); else { tarjan(y); low[x]=min(low[x],low[y]); if (low[y]>=dfn[x]) { int t=s.top(); if (t==y) { g[x][1]+=max(g[y][0],0); g[x][0]+=max(max(g[y][1],g[y][0]),0); continue; } int pret=t; s.pop(); f[t][1][1]=g[t][1]; f[t][0][0]=g[t][0]; f[t][1][0]=f[t][0][1]=-INF; do { t=s.top(); s.pop(); f[t][1][0]=g[t][1]-max(g[pret][0],0)+max(f[pret][0][0],0); f[t][0][0]=g[t][0]-max(max(g[pret][0],g[pret][1]),0)+max(max(f[pret][0][0],f[pret][1][0]),0); f[t][1][1]=g[t][1]-max(g[pret][0],0)+max(f[pret][0][1],0); f[t][0][1]=g[t][0]-max(max(g[pret][0],g[pret][1]),0)+max(max(f[pret][0][1],f[pret][1][1]),0); pret=t; } while (t!=y); g[x][1]+=max(f[y][0][0],0); g[x][0]+=max(max(max(f[y][0][0],f[y][0][1]),max(f[y][1][0],f[y][1][1])),0); } else { g[x][1]+=max(g[y][0],0); g[x][0]+=max(max(g[y][1],g[y][0]),0); } } } } } G; int main() { tn=n=read(); m=read(); for (ri i=1;i<=m;i++) { int u=read(),v=read(); G.add_edge(u,v); } for (ri i=1;i<=n;i++) a[i]=read(); G.cnt=0; G.tarjan(1); int ans=0; for (ri i=1;i<=n;i++) ans=max(ans,max(G.g[i][0],G.g[i][1])); printf("%d ",ans); }