problem:
给定(n)个布尔型变量(x_i)以及他们之间的逻辑关系例如((x_jlor x_i) land (lnot x_plor x_q)……),求出一组合法解
solution:
显然每个点都有两种状态:真和假。这样我们可以把(n)个点拆分成(2n)个点,(x)和(lnot x).
对于每一对关系((ilor j)),我们就可以( eg iLongrightarrow j)和(lnot jLongrightarrow i)这样连出两条边
那么现在变量(x)有四种连边的情况:
-
(x)和(lnot x),此时之间没有边,随便取
-
(xLongrightarrow lnot x),此时(x)为假
-
(lnot xLongrightarrow x),此时(x)为真
-
(xLongrightarrow lnot x)且(lnot xLongrightarrow x),(x)又为真又为假,无解
那么如何求解的情况呢??看看上面的第四种情况就可以联想到(tarjan)s缩点,如果(x)和(lnot x)在同一个强联通分量中就无解
如果(x)所在的强连通分量的拓扑序在(lnot x)所在的强连通分量之后(x)为真(并不会证明)。
另外拓扑序其实就是(tarjan)染色的逆序,这样我们只需要判断(x)的颜色(<)(lnot x)的颜色就ok了
例题:
考虑对每个夫妇建两个点 (x),(lnot x),(x)表示妻子与新郎同侧,(lnot x)则表示丈夫与新郎同侧。
那么对于一对关系((a,b)),(a)向(lnot b),(b)向(lnot a)各连一条边,表示若(a)与新郎同侧,那么(b)不能与新郎同侧,也就是(lnot b)一定要与新郎同侧,反之亦然。
输出时反过来,即若与新郎同侧的是妻子,那么与新娘同侧的(也就是需要输出的人)就是丈夫
(快读这东西就离谱)
code:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int read(){
int x = 1,a = 0;char ch = getchar();
while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
return x*a;
}
const int maxn = 4e5+10;
int n,m;
int getnum(int x){
if (x > n) return x - n;
return x + n;
}
struct node{
int to,nxt;
}ed[maxn*2];
int head[maxn*2],tot;
void add(int u,int to){
ed[++tot].to = to;
ed[tot].nxt = head[u];
head[u] = tot;
}
int col[maxn*2],top,dfn[maxn*2],low[maxn*2],color,cnt,st[maxn*2];
void tarjan(int x){
dfn[x] = low[x] = ++cnt;
st[++top] = x;
for (int i = head[x];i;i = ed[i].nxt){
int to = ed[i].to;
if (!dfn[to]){
tarjan(to);
low[x] = min(low[x],low[to]);
}
else if (!col[to]) low[x] = min(low[x],dfn[to]);
}
if (dfn[x] == low[x]){
++color;
int y;
while (y = st[top--]){
col[y] = color;
if (x == y) break;
}
}
}
bool flag;
void init(){
memset(head,0,sizeof(head));
memset(ed,0,sizeof(ed));
memset(col,0,sizeof(st));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(st,0,sizeof(st));
color = cnt = tot = top = 0;
flag = true;
}
int main(){
while (~scanf ("%d%d",&n,&m)&&(n+m) != 0){
init();
for (int i = 1;i <= m;i++){
char op;
int u ;scanf("%d",&u);scanf ("%c",&op);u++;
if (op == 'h') u+=n;
int v ;scanf("%d",&v);scanf ("%c",&op);v++;
if (op == 'h') v+=n;
add(u,getnum(v));
add(v,getnum(u));
}
add(1,1+n);
for (int i = 1;i <= 2*n;i++){
if (!dfn[i]) tarjan(i);
}
for (int i = 1;i <= n&&flag;i++){
if (col[i] == col[i+n]) flag = false;
}
if (!flag){printf("bad luck
");continue;}
for (int i = 2;i <= n;i++){
if (col[i] > col[i+n]) printf("%dw ",i-1);
else printf("%dh ",i-1);
}
puts("");
}
return 0;
}