BZOJ2503: 相框
Description
T君不满足于焊接奇形怪状的作品,强烈的破坏欲驱使他拆掉这个作品,然后将之焊接成规整的形状。这会儿,T君正要把这个怪物改造成一个环形,当作自己的相框,步骤如下:
T君约定了两种操作:
1. 烧熔一个焊点:使得连接在焊点上的某些导线相分离或保持相连(可以理解为:把焊点上的导线划分为若干个类,相同类中的导线相连,不同类之间的导线相离)
2. 将两根导线的自由端(即未与任何导线相连的一端)焊接起来。
例如上面的步骤中,先将A点烧熔,使得导线1与导线2、4点分离;再将D点烧熔,使得4、5与3、7相离;再烧熔E,使7与6、8相离;最后将1、7相连。
T君想用最少的操作来将原有的作品改造成为相框(要用上所有的导线)。
Input
输入文件的第一行共有两个整数n和m — 分别表示原有的作品的焊点和导线的数量 (0 ≤ n ≤ 1 000, 2 ≤ m ≤ 50 000)。焊点的标号为1~n。 接下来的m行每行共有两个整数 — 导线两端所连接的两个焊点的标号,若不与任何焊点相连,则将这一端标号为0。
原有的作品可能不是连通的。
某些焊点可能只有一根导线与之相连,在该导线的这一端与其他导线相连之前,这些焊点不允许被烧熔。
某些焊点甚至没有任何导线与之相连,由于T君只关心导线,因此这些焊点可以不被考虑。
Output
输出文件只包含一个整数——表示T君需要将原有的作品改造成相框的最少步数。
Sample Input
1 2
1 3
3 4
1 4
4 6
5 6
4 5
1 5
Sample Output
HINT
30%的数据中n≤10;
100%的数据中n≤1000。
这个还是挺考思路的,还要求一些图论性质:
首先我们要把每个联通块拆开,又因为要构成一个简单环,所以要拆成链状:
像这样
那么对于每一个欧拉回路,我们熔掉一个点就可以搞成上面n多个这东西
对于一个奇度点,我们拆一次也可以搞成这样(题目说了随便选边)
然后就可以合并了
代码如下:
//MT_LI #include<cmath> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; int fa[110000]; int findfa(int x) { if(fa[x]!=x)fa[x]=findfa(fa[x]); return fa[x]; } int n,m; int v[110000],er[110000];//是不是欧拉图,有没有被拆过 int degree[110000]; 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)x=++n,fa[x]=x; if(!y)y=++n,fa[y]=y; degree[x]++;degree[y]++; int fx=findfa(x),fy=findfa(y); if(fx==fy)continue; fa[fy]=fx; } int ans=0,sum=0; for(int i=1;i<=n;i++) { if(!degree[i])continue; if(degree[i]&1) { sum++; er[findfa(i)]=1; } if(degree[i]>2) { ans++; v[findfa(i)]=1; } } int cnt=0; for(int i=1;i<=n;i++) if(fa[i]==i&°ree[i]) cnt++; for(int i=1;i<=n;i++) if(cnt>1&°ree[i]&&fa[i]==i&&!er[i]) { sum+=2; if(!v[i])ans++; } printf("%d\n",ans+sum/2); return 0; }