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;
}