1. 程式人生 > >[COGS2443] [HZOI 2016]MC之旅:逃離基友

[COGS2443] [HZOI 2016]MC之旅:逃離基友

HZOI是衡水中學資訊學奧林匹克競賽的縮寫。

題意

有n對寶石,給出所有寶石之間的限制關係以下列格式,要求所有表示式值為真

 1: i       //表示編號為i的鑽石礦一定需要挖去
 2: not i   //表示編號為i的鑽石礦一定不可以被挖去
 3: i and j
 4: i and (not j)
 5: i or j
 6: i or (not j)
 7: not (i and j)
 8: not (i or j)
 9: i xor j //表示異或,即當i=true,j=false或i=false,j=true是表示式為true
10: not (i xor j)
11: i xor (not j)
12: (not i) or (not j)

要求選出n個寶石,每對寶石中選出一個,且滿足所有限制,且字典序最小,輸出方案;無答案輸出“die”

題解

紅果果的2-SAT問題。
講解在這裡
這道題用到了O(nm)的演算法,按照字典序列舉再檢查,但實際複雜度低於O(nm)。

程式碼

/// by ztx
/// blog.csdn.net/hzoi_ztx
// 2-SAT
#include <bits/stdc++.h>
#define Rep(i,l,r) for(i=(l);i<=(r);++i)
#define rep(i,l,r) for(i=(l);i< (r);++i)
#define Rev(i,r,l) for(i=(r);i>=(l);--i) #define rev(i,r,l) for(i=(r);i> (l);--i) #define Each(i,v) for(i=v.begin();i!=v.end();++i) #define r(x) read(x) typedef long long ll ; typedef double lf ; int CH , NEG ; template <typename TP>inline void read(TP& ret) { ret = NEG = 0 ; while
(CH=getchar() , CH<'!') ; if (CH == '-') NEG = true , CH = getchar() ; while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ; if (NEG) ret = -ret ; } #define kN 10010LL #define kM 10010LL #define x(i) (i*2) #define y(i) (i*2+1) #define t(p) e[0][p] #define n(p) e[1][p] template<typename TP>inline bool MA(TP&a,const TP&b) { return a < b ? a=b,true : false; } template<typename TP>inline bool MI(TP&a,const TP&b) { return a > b ? a=b,true : false; } int e[2][kM*4], st[kN*2], te = 0; inline void Add(int u,int v) { te ++ , t(te) = v, n(te) = st[u], st[u] = te; } int dfn[kN*2], low[kN*2], idx, sta[kN*2], top, mark[kN*2], cnt, belong[kN*2]; bool del[kN*2], ins[kN*2]; void dfs(int u) { int p, v; dfn[u] = low[u] = ++idx; sta[++top] = u, ins[u] = true; for (p = st[u]; v = t(p), p; p = n(p)) if (!dfn[v]) dfs(v), MI(low[u],low[v]); else if (ins[v]) MI(low[u],dfn[v]); if (dfn[u] == low[u]) for (cnt++; u!=v;) { v = sta[top--], ins[v] = false; belong[v] = cnt; } } bool dfs2(int u) { if (mark[u] == 1) return true; if (mark[u] == 2) return false; if (del[u]) return false; sta[++top] = u, mark[u] = 1, mark[u^1] = 2; for (int p = st[u]; p; p = n(p)) if (!dfs2(t(p))) return false; return true; } int main() { freopen("T3_.in","r",stdin), freopen("T3_.out","w",stdout); int n, m, i, t, a, b; while (scanf("%d%d", &n, &m) != EOF) { /// clear te = 0; rev (i,y(n),1) del[i] = dfn[i] = mark[i] = st[i] = 0; top = cnt = idx = 0; /// building1 while (m --> 0) { r(t), r(a), ++ a; // x_i = a*2 y_i = a*2+1 if (t == 1) { del[a^1] = true; continue; } if (t == 2) { del[a] = true; continue; } r(b), ++ b; if (t == 7 || t == 12) { Add(a,b^1), Add(b,a^1); continue; } if (t == 10 || t == 11) { Add(a,b), Add(b,a), Add(a^1,b^1), Add(b^1,a^1); continue; } if (t & 1) { if (t == 3) del[a^1] = del[b^1] = true; else if (t == 5) Add(a^1,b), Add(b^1,a); else if (t == 9) Add(a,b^1), Add(b,a^1), Add(a^1,b), Add(b^1,a); } else { if (t == 4) del[a^1] = del[b] = true; else if (t == 6) Add(a^1,b^1), Add(b,a); else if (t == 8) del[a] = del[b] = true; } } /// first judge Rep (i,1,n) if (del[x(i)] && del[y(i)]) { puts("die"); goto CON; } /* /// Tarjan rev (i,y(n),1) if (!dfn[i]) dfs(i); /// second judge Rep (i,1,n) if (belong[x(i)] == belong[y(i)]) { puts("die"); goto CON; } 實測不用縮點判斷更快。。。 */ /// building2 don't need /*rev (i,y(n),1) if (!del[i]) for (a = st[i]; b = t(a), a; a = n(a)) if (!del[b] && belong[b] != belong[i]) Add2(belong[b],belong[i]);*/ /// O(n*m) Rep (i,1,n) if (!mark[x(i)]) if (top = 0, !dfs2(x(i))) { Rep (a,1,top) mark[sta[a]] = mark[sta[a]^1] = 0; if (top = 0, !dfs2(y(i))) { puts("die"); goto CON; } } Rep (i,1,n) printf("%d ", mark[x(i)]==1? x(i)-1: y(i)-1); puts(""); CON:; } fclose(stdin),fclose(stdout); END: getchar(), getchar(); return 0; }