2-sat
理解
内含三道例题hdu
2-sat图具有对称性
https://wenku.baidu.com/view/9c0afe2b3169a4517723a353.html
ppt上的题
和平委员会
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=2e6+10;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int nxt[N<<1],to[N<<1],tot,hd[N];
inline void add(int x,int y) {
to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int n,m;
int dfn[N],low[N],dfn_cnt;
int st[N],top,scc,col[N];
bool vis[N];
void tarjan(int x) {
vis[x]=1;
st[++top]=x;
dfn[x]=low[x]=++dfn_cnt;
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(!dfn[y]) {
tarjan(y);
low[x]=min(low[x],low[y]);
} else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]) {
scc++;
while(st[top+1]!=x) {
col[st[top]]=scc;
vis[st[top--]]=0;
}
}
}
int get(int x) {
return (x%2)?x+1:x-1;
}
int main() {
n=read(),m=read();
int x,y;
for(int i=1;i<=m;i++) {
x=read();y=read();
add(x,get(y));
add(y,get(x));
}
for(int i=1;i<=2*n;i++)
if(!dfn[i]) tarjan(i);
for(int i=1;i<=2*n;i++)
if(col[i]==col[get(i)]) {
puts("NIE");
return 0;
}
for(int i=1;i<=2*n;i++)
if(col[i]>col[get(i)])
printf("%d
",get(i));
return 0;
}
模板
同模板:
https://www.luogu.com.cn/problem/P4171
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=2e6+10;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int nxt[N<<1],to[N<<1],tot,hd[N];
inline void add(int x,int y) {
to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int n,m;
int dfn[N],low[N],dfn_cnt;
int st[N],top,scc,col[N];
bool vis[N];
void tarjan(int x) {
vis[x]=1;
st[++top]=x;
dfn[x]=low[x]=++dfn_cnt;
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(!dfn[y]) {
tarjan(y);
low[x]=min(low[x],low[y]);
} else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]) {
scc++;
while(st[top+1]!=x) {
col[st[top]]=scc;
vis[st[top--]]=0;
}
}
}
int main() {
n=read(),m=read();
int a,b,x,y;
for(int i=1;i<=m;i++) {
a=read(),x=read(),b=read(),y=read();
//若选了a=1,则b=0,若b=1,则a=0
if(x==0&&y==0) add(a+n,b),add(b+n,a);
if(x==1&&y==1) add(b,a+n),add(a,b+n);
if(x==1&&y==0) add(a,b),add(b+n,a+n);
if(x==0&&y==1) add(a+n,b+n),add(b,a);
}
for(int i=1;i<=2*n;i++)
if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++)
if(col[i]==col[i+n]) {
puts("IMPOSSIBLE");
return 0;
}
puts("POSSIBLE");
for(int i=1;i<=n;i++)
if(col[i]>col[i+n]) printf("1 ");
else printf("0 ");
//强联通分量编号越小 -> 拓扑序越大 ->越优
return 0;
}
题目:
POJ 3207
POJ 3683
连边好题
1 & 0 =0 ,0 & 1 = 0 , 0 & 0 = 0
1 & 1 =1
1 or 1 = 1 , 0 or 1 = 1 , 1 or 0 = 1
0 or 0 =0
0 ^ 1 =1 , 1 ^ 0 = 1
1 ^ 1 = 0,0 ^ 0 = 0
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=2e6+10;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int nxt[N<<1],to[N<<1],tot,hd[N];
inline void add(int x,int y) {
to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int n,m;
int dfn[N],low[N],dfn_cnt;
int st[N],top,scc,col[N];
bool vis[N];
void tarjan(int x) {
vis[x]=1;
st[++top]=x;
dfn[x]=low[x]=++dfn_cnt;
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(!dfn[y]) {
tarjan(y);
low[x]=min(low[x],low[y]);
} else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]) {
scc++;
while(st[top+1]!=x) {
col[st[top]]=scc;
vis[st[top--]]=0;
}
}
}
int main() {
n=read(),m=read();
char s[6];
for(int i=1;i<=m;i++) {
int a=read()+1,b=read()+1,c=read();
scanf("%s",s);
if(s[0]=='A') {
if(c==1) {
add(a,a+n),add(b,b+n);
} else {
add(a+n,b),add(b+n,a);
}
}
if(s[0]=='O') {
if(c==1) {
add(a,b+n),add(b,a+n);
} else {
add(a+n,a),add(b+n,b);
}
}
if(s[0]=='X') {
if(c==1) {
add(a,b+n),add(b,a+n);
add(a+n,b),add(b+n,a);
} else {
add(a+n,b+n),add(b+n,a+n);
add(a,b),add(b,a);
}
}
}
for(int i=1;i<=2*n;i++)
if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++)
if(col[i]==col[i+n]) {
puts("NO");
return 0;
}
puts("YES");
return 0;
}
Riddle
对于部分点的连边,暴力是O(N^2)的,
正解:https://www.luogu.com.cn/blog/lhm126/solution-p6378
新加一个前缀状态
详见代码——简直了
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=4e6+10;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int nxt[N<<1],to[N<<1],tot,hd[N];
inline void add(int x,int y) {
to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int n,m,k;
int dfn[N],low[N],dfn_cnt;
int st[N],top,scc,col[N];
bool vis[N];
void tarjan(int x) {
vis[x]=1;
st[++top]=x;
dfn[x]=low[x]=++dfn_cnt;
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(!dfn[y]) {
tarjan(y);
low[x]=min(low[x],low[y]);
} else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]) {
scc++;
while(st[top+1]!=x) {
col[st[top]]=scc;
vis[st[top--]]=0;
}
}
}
int pre1[N],pre0[N],a[N];
int main() {
n=read(),m=read();k=read();
int x,y;
//不加 0, +n 1 选,+2*n 之前不选,+3*n之前选
for(int i=1;i<=m;i++) {
x=read(),y=read();
add(x,y+n),add(y,x+n);
}
while(k--) {
int w=read();
for(int i=1;i<=w;i++)
a[i]=read(),add(a[i]+n,a[i]+3*n),add(a[i]+2*n,a[i]);
//现在选那么之前必须选,之前不选则现在肯定不能选
for(int i=2;i<=w;i++) {
add(a[i-1]+3*n,a[i]+3*n);//之前的之前选->之前选
add(a[i]+2*n,a[i-1]+2*n);//之前不选->之前的之前也不选
add(a[i-1]+3*n,a[i]);//之前的之前选->现在不选
add(a[i]+n,a[i-1]+2*n);//现在不选->之前的之前不选
}
}
for(int i=1;i<=4*n;i++)
if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++)
if(col[i]==col[i+n]||col[i+3*n]==col[i+2*n]) {
puts("NIE");
return 0;
}
puts("TAK");
return 0;
}
poj3683wait
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int u = 2010, w = 3000010;
int ver[w], Next[w], head[u], dfn[u], low[u], c[u], s[u], ins[u];
int val[u], deg[u], opp[u], S[u], T[u], D[u];
int n, m, tot, num, t, p;
// 原图加边
void add(int x, int y) {
ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}
void tarjan(int x) {
dfn[x] = low[x] = ++num;
s[++p] = x, ins[x] = 1;
for (int i = head[x]; i; i = Next[i])
if (!dfn[ver[i]]) {
tarjan(ver[i]);
low[x] = min(low[x], low[ver[i]]);
}
else if (ins[ver[i]])
low[x] = min(low[x], low[ver[i]]);
if (dfn[x] == low[x]) {
t++; int y;
do { y = s[p--], ins[y] = 0; c[y] = t; } while (x != y);
}
}
bool overlap(int a, int b, int c, int d) {
if (a >= c&&a<d || b>c&&b <= d || a <= c&&b >= d) return 1;
return 0;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
int sh, sm, th, tm;
scanf("%d:%d %d:%d %d", &sh, &sm, &th, &tm, &D[i]);
S[i] = sh * 60 + sm; T[i] = th * 60 + tm;
}
for (int i = 1; i < n; i++)
for (int j = i + 1; j <= n; j++) {
if (overlap(S[i], S[i] + D[i], S[j], S[j] + D[j]))
add(i, n + j), add(j, n + i);
if (overlap(S[i], S[i] + D[i], T[j] - D[j], T[j]))
add(i, j), add(n + j, n + i);
if (overlap(T[i] - D[i], T[i], S[j], S[j] + D[j]))
add(n + i, n + j), add(j, i);
if (overlap(T[i] - D[i], T[i], T[j] - D[j], T[j]))
add(n + i, j), add(n + j, i);
}
for (int i = 1; i <= 2 * n; i++)
if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; i++) {
if (c[i] == c[n + i]) { puts("NO"); return 0; }
opp[i] = n + i, opp[n + i] = i;
}
puts("YES");
// 构造方案
for (int i = 1; i <= 2 * n; i++)
val[i] = c[i] > c[opp[i]];
// 输出最终结果
for (int i = 1; i <= n; i++)
if (val[i] == 0) printf("%02d:%02d %02d:%02d
",
S[i] / 60, S[i] % 60,
(S[i] + D[i]) / 60, (S[i] + D[i]) % 60);
else printf("%02d:%02d %02d:%02d
",
(T[i] - D[i]) / 60, (T[i] - D[i]) % 60,
T[i] / 60, T[i] % 60);
return 0;
}
POJ 3648
POJ 2723
POJ 2749
难题:
游戏
[ZJOI2016] 旅行者