[CodeForces 141E]Clearing Up(Kruskal變式)

題目

Clearing Up

題目大意

給出一個nn1n1031\leq n\leq 10^3)個點mm1m1051\leq m\leq 10^5)條邊的圖,每條邊有個顏色,要麼是S,要麼是M,要求一個該圖的生成樹,使得兩種顏色的邊各佔一半。

分析

nn是偶數時無解。

舉個例子

原圖

原圖(紅S,黑M):
舉個例子-原圖

第一步

先用Kruskal把S邊做一個生成樹:
步驟1

第二步

然後在上面的基礎上繼續Kruskal插入所有可以插入的M邊:
步驟2

第三步

然後對剩下的M邊,用Kruskal進行破圈:

  • 先強制插入該邊:
    插入
  • 然後對之前的答案圖跑Kruskal,得到一條現在不能用的S邊(下圖中加粗的邊):Kruskal
  • 從之前的答案中刪除這條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");
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章