BZOJphp
除了普通的动态规划之外,这题还能够用仙人掌的作法来作。
这里没有必要把圆方树给创建出来
\(Tarjan\)的本质其实就是一个构建\(dfs\)树的过程
因此咱们在\(Tarjan\)的过程当中求解就好了
咱们设\(f[i][0/1]\)表示当前节点为\(i\),选或不选的子树的最大独立集
当一条边是树边的时候,转移和树上的转移相同。
不然暂时不转移。
当咱们作完当前点,发现它是一个环的最顶端的时候,咱们须要从新对于这个环计算一遍答案。
咱们须要明白一点:对于环上的节点,只与环有关,挂在环外面的子树能够直接计算在一块儿。ios
如今考虑对于环如何从新计算答案
从这个环的最底端开始往上跳,每次合并一次答案
先考虑如何计算最顶端不选
这样子最底端选或者不选是没有关系的。
维护两个变量\(f_0,f_1\),表示当前点选或者不选的答案
向上转移和树上的转移就是同样的了。
把算出来的\(f_0\)直接加给顶点
而后计算顶端选,
那么最底下的那个点就必定不能选,直接令\(f_1\)初值为\(-\infty\)就行了spa
这样子作完就至关于把环给单独拎出来考虑,
而后就变成了树上的\(dp\)了code
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define ll long long #define RG register #define MAX 55555 inline int read() { RG int x=0,t=1;RG char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } struct Line{int v,next;}e[MAX*3]; int h[MAX],cnt=1; int n,m,fa[MAX],f[MAX][2],dfn[MAX],low[MAX],tim; inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;} void dp(int u,int y) { int t0,t1,f0=0,f1=0; for(int i=y;i!=u;i=fa[i]) { t0=f0+f[i][0];t1=f1+f[i][1]; f0=max(t0,t1);f1=t0; } f[u][0]+=f0; f0=0;f1=-1e9; for(int i=y;i!=u;i=fa[i]) { t0=f0+f[i][0];t1=f1+f[i][1]; f0=max(t0,t1);f1=t0; } f[u][1]+=f1; } void dfs(int u,int ff) { fa[u]=ff;dfn[u]=low[u]=++tim; f[u][1]=1;f[u][0]=0; for(int i=h[u];i;i=e[i].next) { int v=e[i].v; if(!dfn[v])dfs(v,u),low[u]=min(low[u],low[v]); else if(v!=ff)low[u]=min(low[u],dfn[v]); if(low[v]>dfn[u]) f[u][1]+=f[v][0],f[u][0]+=max(f[v][0],f[v][1]); } for(int i=h[u];i;i=e[i].next) if(fa[e[i].v]!=u&&dfn[u]<dfn[e[i].v]) dp(u,e[i].v); } int main() { n=read();m=read(); for(int i=1;i<=m;++i) { int u=read(),v=read(); Add(u,v);Add(v,u); } dfs(1,0); printf("%d\n",max(f[1][0],f[1][1])); return 0; }