題目
題目大意
給出一個()個點()條邊的圖,每條邊有個顏色,要麼是S
,要麼是M
,要求一個該圖的生成樹,使得兩種顏色的邊各佔一半。
分析
是偶數時無解。
舉個例子
原圖
原圖(紅S
,黑M
):
第一步
先用Kruskal把S
邊做一個生成樹:
第二步
然後在上面的基礎上繼續Kruskal插入所有可以插入的M
邊:
第三步
然後對剩下的M
邊,用Kruskal進行破圈:
- 先強制插入該邊:
- 然後對之前的答案圖跑Kruskal,得到一條現在不能用的
S
邊(下圖中加粗的邊): - 從之前的答案中刪除這條
S
邊,加入這條M
邊:
於是就成功地把一個S
邊環成了M
邊
用這個操作不斷替換,直到達成條件。
代碼
#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9') f|=c=='-',c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return f?-x:x;
}
#define MAXN 1000
#define MAXM 100000
#define NO return puts("-1"),0
int N,M;
struct Edge{
int u,v,c;
}E[MAXM+5];
struct DSU{
int fa[MAXN+5];
DSU(){}
DSU(int n){
for(int i=1;i<=n;i++)
fa[i]=i;
}
int Find(int x){
return fa[x]==x?x:fa[x]=Find(fa[x]);
}
bool Same(int x,int y){
return Find(x)==Find(y);
}
void Union(int x,int y){
fa[Find(x)]=Find(y);
}
};
set<int> Ans;
int Kruskal(int id){
DSU tmp(N);
tmp.Union(E[id].u,E[id].v);
for(int i:Ans){
int u=E[i].u,v=E[i].v;
if(!tmp.Same(u,v))
tmp.Union(u,v);
else if(E[i].c)
return i;
}
return -1;
}
void Output(bool sign){
printf("%d\n",Ans.size());
for(int i:Ans)
if(sign)
printf("(%d %d %c)\n",E[i].u,E[i].v,E[i].c?'S':'M');
else
printf("%d ",i);
}
int main(){
N=read(),M=read();
for(int i=1;i<=M;i++){
int u=read(),v=read();
char str[10];scanf("%s",str);
E[i].u=u,E[i].v=v,E[i].c=str[0]=='S';
}
if((N-1)&1)
NO;
DSU C(N);
int cnt0=0,cnt1=0,Half=(N-1)/2;
for(int i=1;i<=M;i++){
int u=E[i].u,v=E[i].v;
if(E[i].c&&!C.Same(u,v)){
cnt1++;
C.Union(u,v);
Ans.insert(i);
}
}//第一步
// Output(1);
for(int i=1;i<=M;i++){
if(E[i].c)
continue;
int u=E[i].u,v=E[i].v;
if(!C.Same(u,v)){
cnt0++;
C.Union(u,v);
Ans.insert(i);
}
// Output(1);
}//第二步
for(int i=1;i<=M&&cnt0<Half;i++){
if(E[i].c)
continue;
int u=E[i].u,v=E[i].v;
int id=Kruskal(i);
if(id!=-1){
cnt1--,cnt0++;
Ans.erase(id),
Ans.insert(i);
}
// Output(1);
}//第三步
if(cnt0==Half&&cnt1==Half)
Output(0);
else
puts("-1");
}