P10779 BZOJ4316 小 C 的独立集 (仙人掌DP)
P10779 BZOJ4316 小 C 的独立集
虽然在圆方树专题中,但我们没有必要把圆方树建出来...
题意
给出一个仙人掌图,求最大独立集
就像是没有上司的舞会超级加倍
没有做过的可以先去康康~
分析
显然树形DP,但是是在仙人掌图上。
至于啥是仙人掌图嘛~
感性的李姐一下就是一棵树塞几个基环且强连通。
引用某dalao的一张图就是:
由于有环,不难想到 Tarjan,所以我们考虑在 Tarjan 的过程中求解。
我们设 \(f[i][1/0]\) 为 \(i\) 节点 \(选/不选\) 的最优答案,
当一条边是树边的时候,正常DP。
否则暂时不转移。
当我们做完当前点,发现它是一个环的最顶端的时候,我们需要重新对这个环计算一遍答案。
从这个环的最底端开始往上跳,每次合并一次答案。
维护两个变量 \(f0,f1\),表示当前点选或者不选的答案。
考虑分讨:
-
若最顶端不选
显然最底端选或者不选是没有影响的。然后正常转移,最后把算出来的 \(f0\)直接加给顶点。
-
若顶端选
那么最底下的那个点就一定不能选,直接令 \(f1\) 初值为 \(-inf\),然后正常转移就好了。
没了。
Code
#include<bits/stdc++.h>
#define 我永远喜欢 return
#define 伊蕾娜 0;
using namespace std;
#define int long long
#define rd read()
#define pii pair<int,int>
#define mkp make_pair
#define psb push_back
inline int read(){
int f=1,x=0;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f=(ch=='-'?-1:1);
for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
return f*x;
}
const int N=1e5+100;
const int inf=0x7fffffff7fffffff;
int n,m,cnt,f[N][2],fa[N];
vector<int> G[N],T[N*2];
int dfn[N],low[N],num;
void dp(int x,int y){
int g0=0,g1=0,f0=0,f1=0;
for(int i=y;i!=x;i=fa[i]){
g0=f0+f[i][0],g1=f1+f[i][1];
f0=max(g0,g1),f1=g0;
}
f[x][0]+=f0;
f0=0,f1=-inf;
for(int i=y;i!=x;i=fa[i]){
g0=f0+f[i][0],g1=f1+f[i][1];
f0=max(g0,g1),f1=g0;
}
f[x][1]+=f1;
}
void tarjan(int x,int ff){
fa[x]=ff;
low[x]=dfn[x]=++num;
f[x][1]=1;
f[x][0]=0;
for(auto to:G[x]){
if(!dfn[to]){
tarjan(to,x);
low[x]=min(low[x],low[to]);
}else if(to!=ff){
low[x]=min(low[x],dfn[to]);
}
if(low[to]>dfn[x]){
f[x][1]+=f[to][0];
f[x][0]+=max(f[to][0],f[to][1]);
}
}
for(auto to:G[x]){
if(fa[to]!=x&&dfn[x]<dfn[to]){
dp(x,to);
}
}
}
signed main(){
n=rd,m=rd;
for(int i=1;i<=m;i++){
int x=rd,y=rd;
G[x].psb(y);
G[y].psb(x);
}
tarjan(1,0);
printf("%lld",max(f[1][0],f[1][1]));
我永远喜欢 伊蕾娜
}