CF662B Graph Coloring

題目鏈接:CF662B Graph Coloring


顯然我們可以對於不同的顏色單獨考慮。

假設我們全部變爲‘B’,那麼如果當前邊是‘B’,我們只能選擇兩個端點全部改變,或者都不改變。
如果當前爲‘R’,那麼兩個端點一定是一個改變,一個不改變。

然後顯然可以直接2-SAT,現在就是怎麼對2-SAT求最小解的問題了。

我們假設點i爲改變,點i+n爲不改變,所以我們對於每個強連通分量單獨考慮,然後記錄聯通塊i,和聯通塊i+n,然後哪個裏面的小於n的點的個數更少,就以哪個爲基準即可。


AC代碼:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e5+10;
int n,m,dfn[N],low[N],vis[N],scc[N],num[N],cnt,co,a[N],b[N],flag[N],mark[N],res;
vector<int> g[N],v[N]; stack<int> s; char op[N][3];
void Tarjan(int x){
	low[x]=dfn[x]=++cnt; vis[x]=1; s.push(x);
	for(int to:g[x]){
		if(!dfn[to]) Tarjan(to),low[x]=min(low[x],low[to]);
		else if(vis[to]) low[x]=min(low[x],dfn[to]);
	}
	if(low[x]==dfn[x]){
		int u; co++;
		do{
			u=s.top(); s.pop(); vis[u]=0; scc[u]=co;
			if(u<=n)	num[co]++;	v[co].push_back(u);
		}while(u!=x);
	}
}
void solve(char ch){
	for(int i=1;i<=2*n;i++)	g[i].clear(),v[i].clear(),scc[i]=dfn[i]=low[i]=0;
	memset(mark,0,sizeof mark),memset(num,0,sizeof num);	cnt=co=0;
	for(int i=1;i<=m;i++){
		if(op[i][0]==ch){
			g[a[i]].push_back(b[i]),g[b[i]].push_back(a[i]);
			g[a[i]+n].push_back(b[i]+n),g[b[i]+n].push_back(a[i]+n);
		}else{
			g[a[i]].push_back(b[i]+n),g[b[i]].push_back(a[i]+n);
			g[a[i]+n].push_back(b[i]),g[b[i]+n].push_back(a[i]);
		}
	}
	for(int i=1;i<=n*2;i++)	if(!dfn[i])	Tarjan(i);
	for(int i=1;i<=n;i++)	if(scc[i]==scc[i+n])	return ;
	int tmp=0;
	for(int i=1;i<=2*n;i++)	if(!mark[i]){
		if(num[scc[i]]<=num[scc[i+n]]){
			tmp+=num[scc[i]];
			for(int j:v[scc[i]]){
				if(j<=n)	mark[j]=1,mark[j+n]=2;
				else	mark[j]=1,mark[j-n]=2;
			}
		}else{
			tmp+=num[scc[i+n]];
			for(int j:v[scc[i+n]]){
				if(j<=n)	mark[j]=1,mark[j+n]=2;
				else	mark[j]=1,mark[j-n]=2;
			}
		}
	}
	if(tmp<res){
		res=tmp;	memset(flag,0,sizeof flag);
		for(int i=1;i<=n*2;i++)	if(mark[i]==1)	flag[i]=1;
	}
}
signed main(){
	cin>>n>>m;	res=1e9;
	for(int i=1;i<=m;i++)	scanf("%d %d %s",&a[i],&b[i],op[i]);
	solve('R'),solve('B');
	if(res==1e9)	return puts("-1"),0;
	cout<<res<<endl;
	for(int i=1;i<=n;i++)	if(flag[i]==1)	printf("%d ",i);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章