题解 P4782 【【模板】2-SAT 问题】
我不会告诉你我在这道题上死了多少次。。。
废话不多说,这道题是一个2-SAT的模板题(不知道2-SAT是什么的小伙伴可以去网上找找,Xie_BR在此不做过多的解释)
首先
对于2-SAT来说,Xie_BR建议使用
Tarjan + Topsort
这是很好很好的方法,简单明了,只有200行
先建边,通过逻辑推理,发现1或0的关系原来可以这么建边:
int x,a,y,b;
scanf("%d%d%d%d",&x,&a,&y,&b);
if(a)
{
if(b)
{
add(x+n,y);
add(y+n,x);
}else
{
add(y,x);
add(x+n,y+n);
}
}else
{
if(b)
{
add(x,y);
add(y+n,x+n);
}else
{
add(x,y+n);
add(y,x+n);
}
}
然后我们需要通过tarjan+缩点合并环,最后计数。
在此十分感谢spj!!!
最后,我们来看看
AC代码:
#include<bits/stdc++.h>
#define MK 5000010
using namespace std;
int n,m;
struct edge
{
int to,next,f;
}e[MK*2];
int h[MK],ei = 0;
void add(int x,int y)
{
ei++;
e[ei].to = y;
e[ei].f = x;
e[ei].next = h[x];
h[x] = ei;
}
stack<int>st;
int dfn[MK],cnt[MK],stindex = 0,tmindex = 0,indegree[MK];
int low[MK],team[MK],col[MK];
void tarjan(int f)
{
stindex++;
low[f] = dfn[f] = stindex;
st.push(f);
for(int i=h[f];i;i=e[i].next)
{
int to = e[i].to;
if(dfn[to]==0)
{
tarjan(to);
low[f] = min(low[f],low[to]);
}else
{
if(team[to]==0)
{
low[f] = min(low[f],dfn[to]);
}
}
}
if(low[f] == dfn[f])
{
tmindex++;
while(1)
{
int top = st.top();
st.pop();
team[top] = tmindex;
cnt[tmindex]++;
if(top==f)
{
break;
}
}
}
return;
}
int opt[MK];
int check()
{
for(int i=1;i<=n;i++)
{
if(team[i]==team[i+n])
{
return 0;
}
opt[team[i]] = team[i+n];
opt[team[i+n]] = team[i];
}
return 1;
}
void suodian()
{
memset(h,0,sizeof(h));
int newei = ei;
ei = 0;
for(register int i=1;i<=newei;i++)
{
int f = e[i].f;
int to = e[i].to;
if(team[f]==team[to])
{
continue;
}
indegree[team[f]]++;
add(team[to],team[f]);
}
return;
}
void topsort()
{
queue<int>qu;
for(int i=1;i<=tmindex;i++)
{
if(indegree[i]==0)
{
qu.push(i);
}
}
while(!qu.empty())
{
int f1 = qu.front();
qu.pop();
if(col[f1]==0)
{
col[f1] = 1;
col[opt[f1]] = 2;
}
for(int i=h[f1];i;i=e[i].next)
{
int to = e[i].to;
indegree[to]--;
if(indegree[to]==0)
{
qu.push(to);
}
}
}
return;
}
void out()
{
printf("POSSIBLE\n");
for(int i=1;i<=n;i++)
{
if(col[team[i]]==1)
{
printf("1 ");
}else
{
printf("0 ");
}
}
return;
}
void dotarjan()
{
for(int i=1;i<=2*n;i++)
{
if(!dfn[i])
{
tarjan(i);
}
}
return;
}
signed main()
{
scanf("%d%d",&n,&m);
while(m--)
{
int x,a,y,b;
scanf("%d%d%d%d",&x,&a,&y,&b);
if(a)
{
if(b)
{
add(x+n,y);
add(y+n,x);
}else
{
add(y,x);
add(x+n,y+n);
}
}else
{
if(b)
{
add(x,y);
add(y+n,x+n);
}else
{
add(x,y+n);
add(y,x+n);
}
}
}
dotarjan();
if(!check())
{
printf("IMPOSSIBLE\n");
return 0;
}
suodian();
topsort();
out();
return 0;
}
不多不多,也就200行。
蒟蒻的想法可能和幼稚,大佬指教啊啊啊啊啊啊啊啊啊!!!