图论小王子小C经常虐菜,特别是在图论方面,经常把小D虐得很惨很惨。
这不,小C让小D去求一个无向图的最大独立集,通俗地讲就是:在无向图中选出若干个点,这些点互相没有边连接,并使取出的点尽量多。
小D虽然图论很弱,但是也知道无向图最大独立集是npc,但是小C很仁慈的给了一个很有特点的图: 图中任何一条边属于且仅属于一个简单环,图中没有重边和自环。小C说这样就会比较水了。
小D觉得这个题目很有趣,就交给你了,相信你一定可以解出来的。
第一行,两个数n, m,表示图的点数和边数。
第二~m+1行,每行两个数x,y,表示x与y之间有一条无向边。
输出这个图的最大独立集。
5 6
1 2
2 3
3 1
3 4
4 5
3 5
2
100% n <=50000, m<=60000
新姿势圆方树get
再也不会在仙人掌题面前一脸懵逼了~
只是…….
说好的这东西好写好调呢咱可是WA了5次啊……
思路:
先扯一波圆方树:
仙人掌这种东西,正如题面所说,仙人掌中任何一条边属于且仅属于一个简单环。
也就是说,环不会边相交。
那么考虑处理环的通用做法:用tarjan缩环。
对于仙人掌,因为它是树形结构,所以咱需要在缩环的同时,保留原本在树上的所有信息。
定义原本就在仙人掌上的点为圆点。
对于每个环,咱新建一个点,来代表这个环。称这样的点为方点。
由于咱仍需要环上的点的信息,所以不能将它们删去。
同时,为了让圆方树最后是一棵树,考虑这样表示一个环:
令一个环上离根节点最近的节点为这个环的根节点。
作为环的代表,方点的父亲节点设为环的根节点的父亲。
对于每个环上的圆点,保留其不属于环的边,其中环的根节点不保留连向父亲的边。
每个环上的圆点向所属环的方点连接一条边,以方点为其父亲。
如果环存在边权,则圆点连向方点的边权设为该点到环的根节点的最短路径,方点连向父亲的边权设为0。
这样再给方点打上标记,圆方树就建好了!
然后是此题思路:
独立集的话,如果是树那就好做多了。
如果是仙人掌嘛……只需要断掉某条边,强制不选这条边其中一个点,跑两树形树形dp,就可以了~
#include
#include
#include
#include
#include
using namespace std;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0' || '9'while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x;
}
const int M=120009;
const int Inf=2147483647;
int n,m,nn;
int pre[M],fa[M],dfn;
bool ring[M];
int to[M],nxt[M],beg[M],tot;
int eto[M],enxt[M],ebeg[M],etot=1;
int f[M][3],stk[M][3],top;
inline void add(int u,int v)
{
to[++tot]=v;
nxt[tot]=beg[u];
beg[u]=tot;
}
inline void adde(int u,int v)
{
eto[++etot]=v;
enxt[etot]=ebeg[u];
ebeg[u]=etot;
}
void dfs(int u,int fae)
{
pre[u]=++dfn;
for(int i=ebeg[u],v;i;i=enxt[i])
if((i^1)!=fae)
{
if(!pre[v=eto[i]])
{
fa[v]=u;
ring[u]=0;
dfs(v,i);
if(!ring[u])
{
add(u,v);
add(v,u);
}
}
else if(pre[v]<=pre[u])
{
int tmp=u;
n++;
while(1)
{
add(n,tmp);
add(tmp,n);
ring[tmp]=1;
if(tmp==v)
break;
tmp=fa[tmp];
}
}
}
}
void dp(int u,int faa)
{
if(u<=nn)
{
f[u][0]=0;
f[u][1]=1;
for(int i=beg[u],v;i;i=nxt[i])
if((v=to[i])!=faa)
{
dp(v,u);
if(v<=nn)
{
f[u][0]+=max(f[v][0],f[v][1]);
f[u][1]+=f[v][0];
}
}
}
else
{
for(int i=beg[u],v;i;i=nxt[i])
if((v=to[i])!=faa)
dp(v,u);
top=0;
for(int i=beg[u];i;i=nxt[i])
{
stk[++top][0]=f[to[i]][0];
stk[top][1]=f[to[i]][1];
}
for(int i=top-1;i;i--)
{
stk[i][0]+=max(stk[i+1][1],stk[i+1][0]);
stk[i][1]+=stk[i+1][0];
}
f[faa][0]=stk[1][0];
for(int i=1;i0]-=max(stk[i+1][1],stk[i+1][0]);
stk[i][1]-=stk[i+1][0];
}
stk[top][1]=0xefefefef;
for(int i=top-1;i;i--)
{
stk[i][0]+=max(stk[i+1][1],stk[i+1][0]);
stk[i][1]+=stk[i+1][0];
}
f[faa][1]=stk[1][1];
}
}
int main()
{
n=read();
m=read();
for(int i=1,u,v;i<=m;i++)
{
u=read();
v=read();
adde(u,v);
adde(v,u);
}
nn=n;
dfs(1,0);
dp(1,0);
printf("%d\n",max(f[1][0],f[1][1]));
return 0;
}