input:
4
5 7
1 2
2 3
3 4
4 5
5 1
1 3
3 5
4 4
1 2
2 3
1 4
3 4
6 7
1 2
1 3
3 4
4 5
1 4
5 6
6 2
2 1
1 2
output:
0111010
1001
0001111
0
题目大意:我们有n个点和m条边,对于每一条边,我们可以把它涂成蓝色,也可以把它涂成红色,现在让我们提供一种涂色方案,即一些边涂成红色,一些边涂成蓝色,只考虑涂成蓝色的边会形成一些连通块,设数量是s1,只考虑涂成红色的边也会形成一些连通块,设数量是s2,让我们设计方案让s1+s2的值最小。
解题思路:一看到连通块,而且是让形成的连通块数量最小,我们可以想到最小生成树,对于这个题我们可以先求一下最小生成树,把形成最小生成树的的边(共n-1条边)涂成一种颜色,假设涂成红色,然后我们再看剩下的边,剩下最多3条边,我们下面的任务就是判断这3条边会不会形成一个环,如果形成了一个环,那么我们就可以选择其中一条边涂成红色,然后选择这条边的一个端点,把剩下的边涂成蓝色,因为如果形成一个环之后去掉一条边不会影响连通块的数量,那么与其让它成为一个环,不如将这个环破坏掉,然后再增加几条边,使边得到最大化利用。
上代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
const int N=2e5+10;
int h[N],ne[N],e[N<<1],idx,w[N<<1];
int t,n,m,p[N],ans[N];
pair<int,int>f[N];
void add(int u,int v,int val)
{
e[idx]=v;
w[idx]=val;//这里的边权表示所在边的编号
ne[idx]=h[u];
h[u]=idx++;
}
int find(int x)
{
if(x!=p[x])
p[x]=find(p[x]);
return p[x];
}
bool merge(int u,int v)
{
int fu=find(u),fv=find(v);
if(fu==fv)
return false;
p[fu]=fv;
return true;
}
int main()
{
cin>>t;
while(t--)
{
scanf("%d %d",&n,&m);
idx=0;
for(int i=1;i<=n;i++)
{
p[i]=i;
h[i]=-1;
}
for(int i=1;i<=m;i++)
ans[i]=1;
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d %d",&u,&v);
f[i]={u,v};
if(merge(u,v))
{
add(u,v,i);
add(v,u,i);
}
else
ans[i]=0;
}
if(n+2==m)
{
set<int>s;
int pos=0;
for(int i=1;i<=m;i++)
{
if(!ans[i])
{
auto temp=f[i];
s.insert(temp.first);
s.insert(temp.second);
pos=i;
}
}
if(s.size()==3)//判断是否形成了一个3元环
{
int u=f[pos].first;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=w[i];
ans[j]=0;
}
ans[pos]=1;
for(int i=1;i<=m;i++)
cout<<ans[i];
cout<<endl;
}
else
{
for(int i=1;i<=m;i++)
cout<<ans[i];
cout<<endl;
}
}
else
{
for(int i=1;i<=m;i++)
cout<<ans[i];
cout<<endl;
}
}
return 0;
}