题意:
给出一个无向图,你需要把边分成红边和蓝边,仅考虑红边时的连通块数量为c1,仅考虑蓝边时的连通块数量为c2,给出一种分配方法,使得c1+c2最小。
思路:
对于每一条新加的边,如果形成了环,会导致这条边被浪费了,即当前这条边可以加入到另外一个集合中使得另外一个集合连通块数量减少,但我们却没有这么做,成环也不会改变当前集合的连通性,因此我们的目标是尽量不出现环。我们可以用并查集维护其中一个集合的连通性,每当要出现环的时候,我们就把这条边加入另外一个集合,但这种做法可能会出现一条边形成两个环,无论加入哪个集合都会成环的情况。因此我们需要把环上的边的颜色也改变一下,那反过来想,我们只要在加边的时候就打乱顺序,那么它成环的概率就大大减小了,因此当两种颜色都可以选的时候,我们随机挑一种颜色即可,如果两种颜色都不能选,那就重头再来。(虽然很玄学,但确实能过)
代码:
#include<bits/stdc++.h>
#define ac return 0
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define endl "\n"
string yes="Yes\n";
string no="No\n";
const int N=1e6+10;
const int MOD=1e9+7;
mt19937 rng(233);
int fa[N],ans[N],fa2[N];
bool vis[N];
struct zz
{
int nxt;
int id;
};
struct edge
{
int u,v;
}e[N];
int found(int p)
{
if(p!=fa[p])
fa[p]=found(fa[p]);
return fa[p];
}
int found2(int p)
{
if(p!=fa2[p])
fa2[p]=found2(fa2[p]);
return fa2[p];
}
void solve()
{
int i,j,m,n;
cin>>n>>m;
for(i=1;i<=n;i++)
{
fa[i]=i;
fa2[i]=i;
}
for(i=1;i<=m;i++)
{
cin>>e[i].u>>e[i].v;
int u=e[i].u,v=e[i].v;
ans[i]=0;
}
while(1)
{
for(i=1;i<=n;i++)
{
fa[i]=i;
fa2[i]=i;
}
for(i=1;i<=m;i++)
{
int u=e[i].u,v=e[i].v;
int fu=found(u),fv=found(v);
int fu2=found2(u),fv2=found2(v);
if(fu!=fv&&fu2!=fv2)
{
int rd=rand();
if(rd%2==1)
{
fa[fu]=fv;
ans[i]=1;
}
else
{
fa2[fu2]=fv2;
ans[i]=0;
}
}
else if(fu!=fv)
{
fa[fu]=fv;
ans[i]=1;
}
else if(fu2!=fv2)
{
fa2[fu2]=fv2;
ans[i]=0;
}
else
{
goto ee;
}
}
break;
ee:;
}
for(i=1;i<=m;i++)
{
cout<<ans[i];
}
cout<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--)
{
solve();
}
ac;
}