bzoj 2503 相框 分类讨论

题目大意:给定一张无向图,每次可以进行以下两种操作: 
1.将一个点分裂成一些点,原先这个点连接的每条边任选一个新点进行连接 
2.将两个度数为1的点合并为1个点 
求将这个图变成一个环的最小操作次数

我们简单画一画可以发现,整个的答案只与度有关。 
如果最后形成了一个环。 
那么环上的点的度一定为2 
不在环上的点则都为不连边的点,度一定为0 
我们一定要拆了所有度大于2的点。 
首先忽略所有度为0的点。 
现在考虑所有的边都在一个连通块内的情况。 
对于一个度大于2的点。 
如果他的度是奇数,那么一定拆成了一堆度为2的点与一个度为1的点。 
如果他的度是偶数,那么一定拆成了一堆度为2的点。 
拆完所有度大于2的点之后。 
度为1的点一定有偶数个,只需要每一次合并两个即可。 
然后我们考虑边不都在一个连通块的情况 
即度不为0的点构成了几个连通块。 
那么对于每一个连通块,我们一定是把这个连通块搞成有两个度为1的点,其余的块内点的度均为2。 
所以这时候与上一种情况有区别的是,上一种情况可能存在操作或者不操作后块内所有的点的度都为2,这时候我们要标记是否拆分了点,因为如果拆分过点的话我们还可以拆分成两个度为1的点,和一堆度为2的点,这只用了一次操作,如果我们后拆的话会多一次操作,注意这里即可。 
其余的正常做。 

 

 1 #include<cstring>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<cstdio>
 6 
 7 #define N 200007
 8 using namespace std;
 9 inline int read()
10 {
11     int x=0,f=1;char ch=getchar();
12     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
13     while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
14     return x*f;
15 }
16 
17 int n,m,ans,blo;
18 int bel[N],flag[N],vis[N],du[N],odd[N];
19 int cnt,hed[N],rea[N],nxt[N];
20 
21 void add(int u,int v)
22 {
23     nxt[++cnt]=hed[u];
24     hed[u]=cnt;
25     rea[cnt]=v;
26 }
27 void dfs(int u,int blo)
28 {
29     vis[u]=1,bel[u]=blo;
30     for (int i=hed[u];i!=-1;i=nxt[i])
31     {
32         int v=rea[i];
33         if (vis[v])continue;
34         dfs(v,blo);
35     }
36 }
37 int main()
38 {
39     memset(hed,-1,sizeof(hed));
40     n=read(),m=read();
41     for(int i=1;i<=m;i++)
42     {
43         int x=read(),y=read();
44         if(x==0)x=++n;
45         if(y==0)y=++n;
46         add(x,y),add(y,x);
47         du[x]++,du[y]++;
48     }
49     for (int i=1;i<=n;i++)
50         if (!vis[i]&&du[i]!=0)
51         {
52             blo++;
53             dfs(i,blo);
54         }
55     for (int i=1;i<=n;i++)
56         if (du[i]==1)
57         {
58             odd[bel[i]]++;
59             if(odd[bel[i]]==4) ans++,odd[bel[i]]=2;
60         }
61     for (int i=1;i<=n;i++)
62     {
63         if(!du[i])continue;
64         if(du[i]>2)
65         {
66             ans++;
67             du[i]=(du[i]&1?1:2);
68             if(du[i]==1)
69             {
70                 odd[bel[i]]++;
71                 if(odd[bel[i]]==4)ans++,odd[bel[i]]=2;
72             }
73             else flag[bel[i]]=1;
74         }
75     }
76     if(blo!=1)
77     {
78         for (int i=1;i<=blo;i++)
79             if (!odd[i]&&!flag[i])ans++;
80         ans+=blo;
81     }
82     else if (odd[1]!=0)ans++;
83     printf("%d\n",ans);
84 }

 

posted @ 2018-01-22 00:07  Kaiser-  阅读(291)  评论(0编辑  收藏  举报