给一个图,问最少加多少条边能让其一笔画出来
如果一个图是连通的且其中度为奇数的点的个数为0个或2个,那么可以一笔画出来
用并查集维护每个联通块的度为奇数的点的个数,如果大于2且为n个,那么需要补n/2条边(每条边让两个度为奇数的点变为偶数),最后再用联通块个数-1条边来将不同的联通块连起来
#include <cstdio>
struct DisjoinSet {
int f[100010];
int num[100010];
int deg[100010];
int odd[100010];
int n;
void clear(int n) {
for (int i=1;i<=n;i++) {
deg[i]=0;
odd[i]=0;
f[i]=i;
num[i]=1;
}
this->n=0;
}
void print(int n) {
printf("deg[i]:");
for (int i=1;i<=n;i++) printf("%d ",deg[i]);
printf("\n");
printf("num[i]:");
for (int i=1;i<=n;i++) printf("%d ",num[i]);
printf("\n");
printf("odd[i]:");
for (int i=1;i<=n;i++) printf("%d ",odd[i]);
printf("\n");
printf("fth[i]:");
for (int i=1;i<=n;i++) printf("%d ",f[i]);
printf("\n");
}
int get(int x) {
if (f[x]==x) return x;
return f[x]=get(f[x]);
}
void tosame(int x,int y) {
deg[x]++;
deg[y]++;
x=get(x);
y=get(y);
if (x==y) return;
if (num[y]==1&&num[x]==1) n++;
else if (num[y]!=1&&num[x]!=1) n--;
num[y]+=num[x];
f[x]=y;
}
int getAns(int n) {
int i;
int ans=this->n-1;
//printf("--%d\n",this->n);
for (i=1;i<=n;i++) {
if (deg[i]%2==1) odd[get(i)]++;
}
for (i=1;i<=n;i++) {
if (odd[i]) ans+=odd[i]/2-1;
}
return ans;
}
};
int n,m;
DisjoinSet c;
int main() {
int t,x,y;
scanf("%d",&t);
while (t--) {
scanf("%d%d",&n,&m);
c.clear(n);
//c.print(n);
while (m--) {
scanf("%d%d",&x,&y);
c.tosame(x,y);
//c.print(n);
}
printf("%d\n",c.getAns(n));
//c.print(n);
}
return 0;
}