题意
题解
定理:设 G G G 为一简单连通平面图,其顶点数 v ≥ 3 v\geq 3 v≥3,其边数为 e e e,那么 e ≤ 3 × v − 6 e\leq 3\times v-6 e≤3×v−6。传送门 证明
根据简单图是平面图的必要条件,在 e > 3 × v − 6 e>3\times v-6 e>3×v−6 时,判定图是非平面图。其余情况, m m m 的规模缩小为与 n n n 同量级。
图中的某条边,画在平面上后(并非一定为直线),可能位于哈密顿回路内部或外部。显然,若无重合顶点的两条边同时在哈密顿回路内部或外部,则当一条边的两个顶点分别位于另一条边将哈密顿回路划分的两个部分时,两条边存在交点。
2-SAT
定义布尔变量 x i x_i xi x i 为 真 ⇔ 边 i 位 于 哈 密 顿 回 路 内 部 x_i为真\Leftrightarrow 边i位于哈密顿回路内部 xi为真⇔边i位于哈密顿回路内部 若两边位于同侧时相交,则有 ¬ ( ( x i ∧ x j ) ∨ ( ¬ x i ∧ ¬ x j ) ) \lnot((x_i\land x_j)\lor(\lnot x_i\land \lnot x_j)) ¬((xi∧xj)∨(¬xi∧¬xj))。建图后 T a r j a n Tarjan Tarjan 求解 2 − S A T 2-SAT 2−SAT。总时间复杂度 O ( T N 2 ) O(TN^2) O(TN2)。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 205, maxm = 10005, maxv = 2 * (3 * maxn - 6), maxe = maxv * maxv;
int T, N, M, X[maxm], Y[maxm], V[maxn], id[maxn];
int tot, head[maxv], to[maxe], nxt[maxe];
int num, dfn[maxv], low[maxv];
int top, st[maxv], scc, sc[maxv];
bool ins[maxv];
inline void add(int x, int y) {
to[++tot] = y, nxt[tot] = head[x], head[x] = tot; }
void tarjan(int x)
{
low[x] = dfn[x] = ++num;
st[++top] = x, ins[x] = 1;
for (int i = head[x]; i; i = nxt[i])
{
int y = to[i];
if (!dfn[y])
tarjan(y), low[x] = min(low[x], low[y]);
else if (ins[y])
low[x] = min(low[x], dfn[y]);
}
if (low[x] == dfn[x])
{
++scc;
int y;
do
{
y = st[top--], ins[y] = 0, sc[y] = scc;
} while (y != x);
}
}
inline bool judge(int i, int j)
{
return (X[i] < X[j] && X[j] < Y[i] && Y[i] < Y[j]) || (X[j] < X[i] && X[i] < Y[j] && Y[j] < Y[i]);
}
int main()
{
scanf("%d", &T);
while (T--)
{
tot = num = top = scc = 0;
memset(head, 0, sizeof(head)), memset(dfn, 0, sizeof(dfn));
memset(sc, 0, sizeof(sc)), memset(ins, 0, sizeof(ins));
scanf("%d%d", &N, &M);
for (int i = 1; i <= M; ++i)
scanf("%d%d", X + i, Y + i);
for (int i = 1; i <= N; ++i)
scanf("%d", V + i), id[V[i]] = i;
for (int i = 1; i <= M; ++i)
{
X[i] = id[X[i]], Y[i] = id[Y[i]];
if (X[i] > Y[i])
swap(X[i], Y[i]);
}
if (M > 3 * N - 6)
{
puts("NO");
continue;
}
for (int i = 1; i <= M; ++i)
for (int j = i + 1; j <= M; ++j)
if (judge(i, j))
add(i, M + j), add(j, M + i), add(M + i, j), add(M + j, i);
int lim = M * 2;
for (int i = 1; i <= lim; ++i)
if (!dfn[i])
tarjan(i);
bool f = 1;
for (int i = 1; i <= M; ++i)
if (sc[i] == sc[M + i])
{
f = 0;
break;
}
puts(f ? "YES" : "NO");
}
return 0;
}
并查集
将问题看做将能否将各条边划分为属于内部一类或属于外部一类。 i i i 代表边 i i i 在哈密顿回路内部, M + i M+i M+i 代表边 i i i 在哈密顿回路外部。并查集求解,总时间复杂度 O ( T N 2 ) O(TN^2) O(TN2)。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 205, maxm = 10005, maxv = 2 * (3 * maxn - 6);
int T, N, M, X[maxm], Y[maxm], V[maxn], id[maxn];
int fa[maxv], sz[maxv];
int find(int x) {
return fa[x] == x ? x : (fa[x] = find(fa[x])); }
void merge(int x, int y)
{
x = find(x), y = find(y);
if (x == y)
return;
if (sz[x] < sz[y])
swap(x, y);
fa[y] = x, sz[x] += sz[y];
}
inline bool judge(int i, int j)
{
return (X[i] < X[j] && X[j] < Y[i] && Y[i] < Y[j]) || (X[j] < X[i] && X[i] < Y[j] && Y[j] < Y[i]);
}
int main()
{
scanf("%d", &T);
while (T--)
{
scanf("%d%d", &N, &M);
for (int i = 1; i <= M; ++i)
scanf("%d%d", X + i, Y + i);
for (int i = 1; i <= N; ++i)
scanf("%d", V + i), id[V[i]] = i;
for (int i = 1; i <= M; ++i)
{
X[i] = id[X[i]], Y[i] = id[Y[i]];
if (X[i] > Y[i])
swap(X[i], Y[i]);
}
if (M > 3 * N - 6)
{
puts("NO");
continue;
}
int lim = M * 2;
for (int i = 1; i <= lim; ++i)
fa[i] = i, sz[i] = 1;
bool f = 1;
for (int i = 1; f && i <= M; ++i)
for (int j = i + 1; j <= M; ++j)
if (judge(i, j))
{
int x = find(i), y = find(j);
if (x == y)
{
f = 0;
break;
}
merge(M + i, j), merge(i, M + j);
}
puts(f ? "YES" : "NO");
}
return 0;
}