diff --git a/省选准备/assets/day1/poj2749.cpp b/省选准备/assets/day1/poj2749.cpp new file mode 100644 index 0000000..cae7adc --- /dev/null +++ b/省选准备/assets/day1/poj2749.cpp @@ -0,0 +1,97 @@ +#include +#include +inline int abs(int x) { return x >= 0 ? x : -x; } +inline int min(int a, int b) { return a < b ? a : b; } +const int inf = 0x3f3f3f3f, maxn = 10010, maxm = 1200010; +int head[maxn], next[maxm], to[maxm], ecnt, n, A, B; +int dfn[maxn], low[maxn], stk[maxn], scc[maxn], top, idx, scccnt; +int sx1, sy1, sx2, sy2, sLen, X[maxn], Y[maxn], hate[maxn][2], like[maxn][2], +d[maxn]; +inline void addEdge(int f, int t) +{ + next[ecnt] = head[f]; + head[f] = ecnt; + to[ecnt] = t; + ecnt++; +} +void tarjan(int x) +{ + dfn[x] = low[x] = ++idx; + stk[top++] = x; + for (int i = head[x]; ~i; i = next[i]) + if (!dfn[to[i]]) + tarjan(to[i]), low[x] = min(low[x], low[to[i]]); + else if (!scc[to[i]]) + low[x] = min(low[x], dfn[to[i]]); + if (dfn[x] == low[x]) + { + scccnt++; + do + scc[stk[--top]] = scccnt; + while (stk[top] != x); + } +} +bool check(int x) +{ + memset(dfn, 0, sizeof(dfn)); + memset(low, 0, sizeof(low)); + memset(scc, 0, sizeof(scc)); + memset(head, -1, sizeof(head)); + ecnt = top = idx = scccnt = 0; + for (int i = 1; i <= n; i++) + for (int j = i + 1; j <= n; j++) + { + int l1 = d[i << 1], l2 = d[i << 1 | 1]; + int r1 = d[j << 1], r2 = d[j << 1 | 1]; + if (l1 + r1 > x) + addEdge(i << 1, j << 1 | 1), addEdge(j << 1, i << 1 | 1); + if (l1 + r2 + sLen > x) + addEdge(i << 1, j << 1), addEdge(j << 1 | 1, i << 1 | 1); + if (l2 + r1 + sLen > x) + addEdge(i << 1 | 1, j << 1 | 1), addEdge(j << 1, i << 1); + if (l2 + r2 > x) + addEdge(i << 1 | 1, j << 1), addEdge(j << 1 | 1, i << 1); + } + for (int i = 1, a, b; i <= A; i++) + { + a = hate[i][0], b = hate[i][1]; + addEdge(a << 1, b << 1 | 1); + addEdge(a << 1 | 1, b << 1); + addEdge(b << 1, a << 1 | 1); + addEdge(b << 1 | 1, a << 1); + } + for (int i = 1, a, b; i <= B; i++) + { + a = like[i][0], b = like[i][1]; + addEdge(a << 1, b << 1); + addEdge(a << 1 | 1, b << 1 | 1); + addEdge(b << 1, a << 1); + addEdge(b << 1 | 1, a << 1 | 1); + } + for (int i = 1; i <= (n << 1); i++) + if (!dfn[i]) + tarjan(i); + for (int i = 1; i <= n; i++) + if (scc[i << 1] == scc[i << 1 | 1]) + return false; + return true; +} +int main() +{ + scanf("%d%d%d%d%d%d%d", &n, &A, &B, &sx1, &sy1, &sx2, &sy2); + sLen = abs(sx1 - sx2) + abs(sy1 - sy2); + for (int i = 1; i <= n; i++) + scanf("%d%d", X + i, Y + i); + for (int i = 1; i <= n; i++) + d[i << 1] = abs(X[i] - sx1) + abs(Y[i] - sy1), + d[i << 1 | 1] = abs(X[i] - sx2) + abs(Y[i] - sy2); + for (int i = 1; i <= A; i++) + scanf("%d%d", &hate[i][0], &hate[i][1]); + for (int i = 1; i <= B; i++) + scanf("%d%d", &like[i][0], &like[i][1]); + int l = 0, r = 8000000, m, ans = -1; + while (l <= r) + check(m = (l + r) >> 1) ? r = (ans = m) - 1 : l = m + 1; + printf("%d\n", ans); + return 0; +} diff --git a/省选准备/assets/day1/poj3648.cpp b/省选准备/assets/day1/poj3648.cpp new file mode 100644 index 0000000..91d2167 --- /dev/null +++ b/省选准备/assets/day1/poj3648.cpp @@ -0,0 +1,65 @@ +#include +#include +inline int min(int a, int b) { return a < b ? a : b; } +const int maxn = 2010, maxm = 500010; +int head[maxn], next[maxm], to[maxm], ecnt; +inline void addEdge(int f, int t) +{ + next[ecnt] = head[f]; + head[f] = ecnt; + to[ecnt] = t; + ecnt++; +} +int dfn[maxn], low[maxn], stk[maxn], scc[maxn], top, idx, scccnt; +void tarjan(int x) +{ + dfn[x] = low[x] = ++idx; + stk[top++] = x; + for (int i = head[x]; ~i; i = next[i]) + if (!dfn[to[i]]) + tarjan(to[i]), low[x] = min(low[x], low[to[i]]); + else if (!scc[to[i]]) + low[x] = min(low[x], dfn[to[i]]); + if (dfn[x] == low[x]) + { + scccnt++; + do + scc[stk[--top]] = scccnt; + while (stk[top] != x); + } +} +int main() +{ + int m, n; + while (scanf("%d%d", &n, &m) && (m + n)) + { + memset(head, -1, sizeof(head)); + memset(dfn, 0, sizeof(dfn)); + memset(low, 0, sizeof(low)); + memset(scc, 0, sizeof(scc)); + idx = top = ecnt = scccnt = 0; + int a1, a2; + char c1, c2; + for (int i = 0; i < m; i++) + { + scanf("%d%c %d%c", &a1, &c1, &a2, &c2); + a1 = a1 << 1 | (c1 == 'h'), a2 = a2 << 1 | (c2 == 'h'); + addEdge(a1, a2 ^ 1), addEdge(a2, a1 ^ 1); + } + addEdge(0, 1); + for (int i = 0; i < (n << 1); i++) + if (!dfn[i]) tarjan(i); + bool flag = true; + for (int i = 0; i < n && flag; i++) + if (scc[i << 1] == scc[i << 1 | 1]) + flag = false; + if (!flag) + puts("bad luck"); + else if (n < 1) + putchar('\n'); + else + for (int i = 1; i < n; i++) + printf("%d%c%c", i, (scc[i << 1] > scc[i << 1 | 1]) ? 'w' : 'h', " \n"[i == n - 1]); + } + return 0; +} diff --git a/省选准备/assets/day1/poj3678.cpp b/省选准备/assets/day1/poj3678.cpp new file mode 100644 index 0000000..65f700d --- /dev/null +++ b/省选准备/assets/day1/poj3678.cpp @@ -0,0 +1,88 @@ +#include +#include +inline int min(int a, int b) { return a < b ? a : b; } +const int maxn = 10010, maxm = 4000010; +int head[maxn], next[maxm], to[maxm], ecnt; +inline void addEdge(int f, int t) +{ + next[ecnt] = head[f]; + head[f] = ecnt; + to[ecnt] = t; + ecnt++; +} +int dfn[maxn], low[maxn], stk[maxn], scc[maxn], top, idx, scccnt; +void tarjan(int x) +{ + dfn[x] = low[x] = ++idx; + stk[top++] = x; + for (int i = head[x]; ~i; i = next[i]) + if (!dfn[to[i]]) + tarjan(to[i]), low[x] = min(low[x], low[to[i]]); + else if (!scc[to[i]]) + low[x] = min(low[x], dfn[to[i]]); + if (dfn[x] == low[x]) + { + scccnt++; + do + scc[stk[--top]] = scccnt; + while (stk[top] != x); + } +} +int main() +{ + int n, m; + while (~scanf("%d%d", &n, &m)) + { + memset(dfn, 0, sizeof(dfn)); + memset(low, 0, sizeof(low)); + memset(scc, 0, sizeof(scc)); + memset(head, -1, sizeof(head)); + ecnt = top = idx = scccnt = 0; + for (int i = 0, u, v, w; i < m; ++i) + { + char op[5]; + scanf("%d%d%d%s", &u, &v, &w, op); + if (op[0] == 'A') + if (w) + { + addEdge(u << 1, v << 1 | 1), addEdge(v << 1, u << 1 | 1); + addEdge(u << 1, v << 1), addEdge(v << 1 | 1, u << 1 | 1); + addEdge(u << 1 | 1, v << 1 | 1), addEdge(v << 1, u << 1); + } + else + { + addEdge(u << 1 | 1, v << 1), addEdge(v << 1 | 1, u << 1); + } + if (op[0] == 'O') + if (w) + { + addEdge(u << 1, v << 1 | 1), addEdge(v << 1, u << 1 | 1); + } + else + { + addEdge(u << 1, v << 1), addEdge(v << 1 | 1, u << 1 | 1); + addEdge(u << 1 | 1, v << 1 | 1), addEdge(v << 1, u << 1); + addEdge(u << 1 | 1, v << 1), addEdge(v << 1 | 1, u << 1); + } + if (op[0] == 'X') + if (w) + { + addEdge(u << 1, v << 1 | 1), addEdge(v << 1, u << 1 | 1); + addEdge(u << 1 | 1, v << 1), addEdge(v << 1 | 1, u << 1); + } + else + { + addEdge(u << 1, v << 1), addEdge(v << 1 | 1, u << 1 | 1); + addEdge(u << 1 | 1, v << 1 | 1), addEdge(v << 1, u << 1); + } + } + for (int i = 0; i < (n << 1); i++) + if (!dfn[i]) tarjan(i); + bool flag = true; + for (int i = 0; i < n && flag; i++) + if (scc[i << 1] == scc[i << 1 | 1]) + flag = false; + puts(flag ? "YES" : "NO"); + } + return 0; +} \ No newline at end of file diff --git a/省选准备/省选基础算法.pdf b/省选准备/省选基础算法.pdf index d4037dc..bcb4313 100644 Binary files a/省选准备/省选基础算法.pdf and b/省选准备/省选基础算法.pdf differ diff --git a/省选准备/省选基础算法.tex b/省选准备/省选基础算法.tex index 9e57ef2..18e0577 100644 --- a/省选准备/省选基础算法.tex +++ b/省选准备/省选基础算法.tex @@ -63,4 +63,25 @@ \subparagraph{\href{http://poj.org/problem?id=1523}{POJ1523} - SPF} 求割点与删除这个点之后有多少个连通分量 \codeinput[Redundant Paths]{assets/day1/poj1523.cpp} + \subsection{2-SAT} + \paragraph{定义} + 给定一个布尔方程,判断是否存在一组布尔变量的取值方案,使得整个方程值为真的问题,被称为布尔方程的可满足性问题(SAT)。SAT问题是NP完全的,但对于一些特殊形式的SAT问题我们可以有效求解。\\ + 我们将下面这种布尔方程称为合取范式: + $$(a\lor b\lor c\lor\cdots)\land(d\lor e\lor f\lor\cdots)\land\cdots$$ + 其中$a,b,c,\cdots$称为文字,它是一个布尔变量或其否定。像$(a\lor b\lor c\lor\cdots)$这样用$\lor$连接的部分称为子句。如果合取范式的每个子句中的文字个数都不超过两个,那么对应的SAT问题又称为\textbf{2-SAT}问题。 + \paragraph{解法} + 对于给定的\textbf{2-SAT}问题,首先利用$\Rightarrow$将每个子句$(a\lor b)$改写成等价形式$(\neg a\Rightarrow b\land a\Rightarrow\neg b)$.这样原布尔公式就变成了把$a\Rightarrow b$形式的布尔公式用$\land$连接起来的形式。\\ + 对每个布尔变量$x$构造两个顶点分别代表$x$与$\neg x$。以$\Rightarrow$关系为边建立有向图。若在此图中$a$点能到达$b$点,就表示$a$为真时$b$也一定为真。因此该图中同一个强连通分量中所含的所有变量的布尔值均相同。\\ + 若存在某个变量$x$,代表$x$与$\neg x$的两个顶点在同一个强连通分量中,则原布尔表达式的值无法为真。\\ + 反之若不存在这样的变量,那么我们先将原图中所有的强连通分量缩为一个点,构出一个新图,新图显然是一个拓扑图,我们求出它的一个拓扑序。那么对于每个变量$x$,\textbf{$$x\text{所在的强连通分量(新图中的点)的拓扑序在}\neg x\text{所在的强连通分量之后}\Leftrightarrow x\text{为真}$$}就是一组合适布尔变量赋值。注意到 Tarjan 算法所求的强连通分量就是按拓扑序的逆序得出的,因此不需要真的缩点建新图求拓扑序,直接利用强连通分量的编号来当做顺序即可。 + \paragraph{练习题} + \subparagraph{\href{http://poj.org/problem?id=3648}{POJ3648} - Wedding} + Additionally, there are several pairs of people conducting adulterous relationships (both different-sex and same-sex relationships are possible) + \codeinput[adulterous relationships]{assets/day1/poj3648.cpp} + \subparagraph{\href{http://poj.org/problem?id=3678}{POJ3678} - Katu Puzzle} + 我什么时候做过这个题? + \codeinput[Katu Puzzle]{assets/day1/poj3678.cpp} + \subparagraph{\href{http://poj.org/problem?id=2749}{POJ2749} - Building roads} + 杀光奶牛问题就会得到解决 + \codeinput[Building roads]{assets/day1/poj2749.cpp} \end{document} \ No newline at end of file