bzoj4316 小C的独立集 (仙人掌独立集,tarjan求无向图点双,圆方树思想)

题意

业界毒瘤求独立集
n<=5e4

圆方树

求点双,然后每个点双建一个方点,原来的点称作圆点,向它所在方点连边
可以证明仙人掌这样搞出来是一棵树,具体看wc2017课件
有一个子仙人掌的概念,到树中就变成了子树
然后就树形dp,碰到方点就把环拿出来dp一下,放回方点的父亲
其实不用把图真正建出来,tarjan的时候搞就行

tarjan求点双

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N=5e4+10,M=6e4+10;
int n,m;
int final[N],to[M*2],nex[M*2],tot;
int stm,low[N],dfn[N];
int S[N],T,f[N][2];
int ans,r[N];
void link(int x,int y) {
    to[++tot]=y,nex[tot]=final[x],final[x]=tot;
}

int z[N][2];
void solve() {
    z[1][0]=f[r[1]][0],z[1][1]=-1e9;
    for (int i=2; i<=r[0]; i++) {
        z[i][0]=f[r[i]][0]+max(z[i-1][0],z[i-1][1]);
        z[i][1]=f[r[i]][1]+z[i-1][0];
    }
    int m0=max(z[r[0]][0],z[r[0]][1]);

    z[1][0]=-1e9,z[1][1]=f[r[1]][1];
    for (int i=2; i<=r[0]; i++) {
        z[i][0]=f[r[i]][0]+max(z[i-1][0],z[i-1][1]);
        z[i][1]=f[r[i]][1]+z[i-1][0];
    }
    int m1=z[r[0]][0];
    f[r[1]][0]=m0,f[r[1]][1]=m1;
}

void tarjan(int x,int w) {
    dfn[x]=low[x]=++stm;
    S[++T]=x;
    for (int i=final[x]; i; i=nex[i]) {
        if ((i^1)==w) continue;
        int y=to[i];
        if (dfn[y]==0) {
            tarjan(y,i);
            low[x]=min(low[x],low[y]);
            if (low[y]>dfn[x]) {
                f[x][0]+=max(f[y][0],f[y][1]);
                f[x][1]+=f[y][0];
                T--;
            } else if (low[y]==dfn[x]) {
                r[r[0]=1]=x;
                while (S[T+1]!=y) r[++r[0]]=S[T--];
                //与边双不一样的是,不能出到x
                //不然可能有一些其他点双的出来
                solve();
            }
        } else low[x]=min(low[x],dfn[y]);
    }
    f[x][1]++;
}

int main() {
    freopen("4316.in","r",stdin);
    tot=1;
    cin>>n>>m;
    for (int i=1; i<=m; i++) {
        int x,y; scanf("%d %d",&x,&y);
        link(x,y); link(y,x);
    }
    for (int i=1; i<=n; i++) if (dfn[i]==0) {
        tarjan(i,0);
        ans+=max(f[i][0],f[i][1]);
    }
    cout<<ans<<endl;
}

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/80908333