题目大意:给定一张无向图,每次可以进行以下两种操作:
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 }