[bzoj4316][圆方树]小C的独立集

Description

图论小王子小C经常虐菜,特别是在图论方面,经常把小D虐得很惨很惨。
这不,小C让小D去求一个无向图的最大独立集,通俗地讲就是:在无向图中选出若干个点,这些点互相没有边连接,并使取出的点尽量多。
小D虽然图论很弱,但是也知道无向图最大独立集是npc,但是小C很仁慈的给了一个很有特点的图:
图中任何一条边属于且仅属于一个简单环,图中没有重边和自环。小C说这样就会比较水了。
小D觉得这个题目很有趣,就交给你了,相信你一定可以解出来的。

Input

第一行,两个数n, m,表示图的点数和边数。 第二~m+1行,每行两个数x,y,表示x与y之间有一条无向边。

Output

输出这个图的最大独立集。

Sample Input

5 6

1 2

2 3

3 1

3 4

4 5

3 5

Sample Output

2

HINT

100% n <=50000, m<=60000

题解

学了圆方树就做个裸题爽一发..
先考虑图是树的情况
显然就是没有上司的舞会..
这是个仙人掌,我们还有非树边的影响
可以用圆方树的思想解决
tarjan的时候,如果找到的是非环,直接计算影响
找到环了就把环拉出来,考虑环的根的影响(因为只有环的根会对上面造成影响..)
扫两遍暴力出环的根选或者不选的影响即可..
没有必要建出圆方树,tarjan的时候顺便做一次就行了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct node{int x,y,next;}a[210000];int len,last[110000];
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
int low[50005],dfn[50005],sta[50005],id,cnt,tp;
int f[50005][2],z[50005][2],r[50005];
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)
{
    low[x]=dfn[x]=++id;sta[++tp]=x;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(dfn[y]==-1)
        {
            tarjan(y);low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x])
            {
                f[x][1]+=f[y][0];
                f[x][0]+=max(f[y][0],f[y][1]);tp--;
            }
            else if(low[y]==dfn[x])//环的根 
            {
                r[r[0]=1]=x;int i;
                do
                {
                    i=sta[tp--];
                    r[++r[0]]=i;
                }while(i!=y);
                solve();
            }
        }
        else low[x]=min(low[x],dfn[y]);
    }
    f[x][1]++;
}
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        ins(x,y);ins(y,x);
    }
    int ans=0;
    memset(dfn,-1,sizeof(dfn));
    for(int i=1;i<=n;i++)if(dfn[i]==-1)
    {
        tarjan(i);
        ans+=max(f[i][0],f[i][1]);
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值