题目传送门
题目大意
给你一张无向图,图中每条边是蓝色或者红色的,让你每次选一个点,就会把与这个点相连的边的颜色反转(红变蓝,蓝变红),求最少步数的方案使得最后所有边的颜色都一样。
思路
好像没有
2
?
s
a
t
2-sat
2?sat 的题解,那我就来一发。
首先分类讨论:要么都变成红色,要么都变成蓝色。
如果一条边的颜色与当前我们期望的颜色相同,那么要么两个点都选,要么都不选,所以如果一个点不选,那么另一个点也不选,一个点选了,另一个点也要选,建图就行了,如下图。
那么同理,如果一条边的颜色与期望的颜色不同,所以,如果一个点选,另一个点就不能选,如果一个点不选,那么另一个点就必须能选,图的话,略吧。
这样我们就理出来一个
2
?
s
a
t
2-sat
2?sat 的模型。
判断无解的情况,这就是
2
?
s
a
t
2-sat
2?sat 的经典操作,判断
u
,
u
′
u,u'
u,u′ 是否处于一个强联通分量即可。
对于有解的情况,还要输出解,那么由于这个图是上下对称的,所以也就是说,如果你选了
i
1
,
i
2
…
,
i
m
,
j
1
′
,
j
2
′
…
,
j
m
′
′
i_1,i_2\dots,i_m,j_1',j_2'\dots,j_{m'}'
i1?,i2?…,im?,j1′?,j2′?…,jm′′? 这个强联通分量,那么你也一定可以不选它,选
i
1
′
,
i
2
′
…
,
i
m
′
,
j
1
,
j
2
…
,
j
m
′
i_1',i_2'\dots,i_m',j_1,j_2\dots,j_{m'}
i1′?,i2′?…,im′?,j1?,j2?…,jm′? 这个强联通分量,那么为了让选的个数尽可能的少,所以看看这两个东西中
u
′
u'
u′ 个数谁多谁少,加到答案里面,然后再把选的这些点放到最后的方案里面就行了。
详见代码。
不知道为什么其他题解说这题细节多,可能我的方法没啥细节。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
void read(){}
template<typename _Tp,typename... _Tps>
void read(_Tp &x,_Tps &...Ar){
x=0;char c=getchar();bool flag=0;
while(c<'0'||c>'9')flag|=(c=='-'),c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(flag)x=-x;
read(Ar...);
}
const int N=1e5+10;
int n,m;
struct zj{
int u,v,w;
}a[N];
struct edges{
int to,nex;
}edge[N*4];
int head[N*2],kk;
void addedge(int u,int v){
edge[++kk]=(edges){v,head[u]};head[u]=kk;
edge[++kk]=(edges){u,head[v]};head[v]=kk;
}
int dfn[N*2],low[N*2],s[N*2],top,cnt,scc[N*2],scct;
void tarjan(int u){
dfn[u]=low[u]=++cnt;s[++top]=u;
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].to;
if(!dfn[v])tarjan(v),low[u]=min(low[u],low[v]);
else if(!scc[v])low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u]){
++scct;
while(s[top]!=u)scc[s[top--]]=scct;
scc[s[top--]]=scct;
}
}
int cnt1,cnt2,k1[N],k2[N],s1[N],s2[N];
bool vis[N];
void dfs(int u){
if(vis[(u-1)%n+1])return;
vis[(u-1)%n+1]=1;u<=n?(k1[++cnt1]=u):(k2[++cnt2]=u-n);
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].to;
dfs(v);
}
}
int get(){
int i;char c[5];bool flag1,flag2;int ans1=0,ans2=0;
for(read(n,m),i=1;i<=m;i++)read(a[i].u,a[i].v),scanf("%s",c),a[i].w=(c[0]=='R');
for(i=1;i<=m;i++)a[i].w?(addedge(a[i].u,a[i].v),addedge(a[i].u+n,a[i].v+n)):(addedge(a[i].u,a[i].v+n),addedge(a[i].u+n,a[i].v));
for(i=1;i<=n*2;i++)if(!dfn[i])tarjan(i);
for(flag1=1,i=1;i<=n;i++)if(scc[i]==scc[i+n])flag1=0;
if(flag1)for(i=1;i<=n;i++){
if(vis[i])continue;
cnt1=cnt2=0;
dfs(i);
if(cnt1<cnt2){
ans1+=cnt1;
while(cnt1)s1[++s1[0]]=k1[cnt1--];
}
else{
ans1+=cnt2;
while(cnt2)s1[++s1[0]]=k2[cnt2--];
}
}
memset(head,0,sizeof(head));kk=0;
memset(dfn,0,sizeof(dfn));cnt=0;
memset(low,0,sizeof(low));
memset(scc,0,sizeof(low));scct=0;
memset(s,0,sizeof(s));top=0;
memset(vis,0,sizeof(vis));
for(i=1;i<=m;i++)a[i].w?(addedge(a[i].u+n,a[i].v),addedge(a[i].u,a[i].v+n)):(addedge(a[i].u,a[i].v),addedge(a[i].u+n,a[i].v+n));
for(i=1;i<=n*2;i++)if(!dfn[i])tarjan(i);
for(flag2=1,i=1;i<=n;i++)if(scc[i]==scc[i+n])flag2=0;
if(flag2)for(i=1;i<=n;i++){
cnt1=cnt2=0;
dfs(i);
if(cnt1<cnt2){
ans2+=cnt1;
while(cnt1)s2[++s2[0]]=k1[cnt1--];
}
else{
ans2+=cnt2;
while(cnt2)s2[++s2[0]]=k2[cnt2--];
}
}
if(!flag1&&!flag2)return printf("-1"),0;
if(!flag1||(flag2&&s1[0]>s2[0]))swap(s1,s2);
for(printf("%d\n",s1[0]),i=1;i<=s1[0];i++)printf("%d ",s1[i]);
return 0;
}
int main(){
return get();
}
温馨提醒
如果有什么笔误之类的,评论或私聊我,希望能帮到大家
谢谢–zhengjun
|