题目
特性
如果原图是一个连通图,并且为欧拉回路,那么把所有点熔开即可。其实上面这句话是废话
一个多条边(>2)连接的点一定要熔。
一个奇数条边相连的点一定是熔完后要接的。
题解
乱搞+并查集
其实上面的几条边相连都可以用“度”来表达。
所以得到一个乱搞算法:
1、每有一个多度点,ans++;
2、每有一个奇度点,cnt++。
对于那些自由边,另开一个点给它们。
还有一个问题,怎么把两个环合成一个呢?
所以先要维护一下连通性,在无向图中,并查集是首选。
对于一个连通块,因为要跟其它块合并,所以它必须断开一个口来,那么会多两个奇度点,cnt+=2。断开口的点需要熔,代价为1,ans++。特别的,如果一个点本身就需要熔(度数大于2),那么它可以考虑熔出两条一端自由的边出来,此时就可以省一次的熔。又特别的,如果一个连通块中本身就有奇度点,那么只要保留它就可以了,这下连合并熔的代价也可以省掉(其实是已经算过一次了)。
有人会问,会不会存在只有一个奇度点的图,这样的话我们还要花费一些代价熔开其它点来制造奇度点?答案是否定的,直接给出定律:一个图的奇度点必为偶数个,可以用所有点度数之和为偶数来证明。
所以要维护一下每个连通块中是否存在多度点和奇度点。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1010,MAXM=50010;
int n,m;
int deg[MAXM*3];
bool bk[MAXM*3],dev[MAXM*3];
int fa[MAXM*3];
int findfa(int x){return x==fa[x]?fa[x]:fa[x]=findfa(fa[x]);}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(x==0) fa[++n]=x=n;if(y==0) fa[++n]=y=n;
deg[x]++;deg[y]++;
fa[findfa(x)]=findfa(y);
}
int ans=0,cnt=0,num=0;
for(int i=1;i<=n;i++)
{
if(deg[i]==0) continue;//debug 度数为0不考虑
if(findfa(i)==i) num++;
if(deg[i]&1) bk[fa[i]]=true,cnt++;
if(deg[i]>2) dev[fa[i]]=true,ans++;
}
if(num!=1)
for(int i=1;i<=n;i++) if(deg[i]!=0 && fa[i]==i && bk[i]==false)
{
cnt+=2;
if(!dev[i]) ans++;
}
ans+=cnt/2;
printf("%d\n",ans);
return 0;
}