P3209 [HNOI2010] 平面图 + 2-SAT / 并查集

题意

传送门 P3209 [HNOI2010] 平面图判定

题解

定理:设 G G G 为一简单连通平面图,其顶点数 v ≥ 3 v\geq 3 v3,其边数为 e e e,那么 e ≤ 3 × v − 6 e\leq 3\times v-6 e3×v6传送门 证明

根据简单图是平面图的必要条件,在 e > 3 × v − 6 e>3\times v-6 e>3×v6 时,判定图是非平面图。其余情况, m m m 的规模缩小为与 n n n 同量级。

图中的某条边,画在平面上后(并非一定为直线),可能位于哈密顿回路内部或外部。显然,若无重合顶点的两条边同时在哈密顿回路内部或外部,则当一条边的两个顶点分别位于另一条边将哈密顿回路划分的两个部分时,两条边存在交点。

2-SAT

定义布尔变量 x i x_i xi x i 为 真 ⇔ 边 i 位 于 哈 密 顿 回 路 内 部 x_i为真\Leftrightarrow 边i位于哈密顿回路内部 xii 若两边位于同侧时相交,则有 ¬ ( ( x i ∧ x j ) ∨ ( ¬ x i ∧ ¬ x j ) ) \lnot((x_i\land x_j)\lor(\lnot x_i\land \lnot x_j)) ¬((xixj)(¬xi¬xj))。建图后 T a r j a n Tarjan Tarjan 求解 2 − S A T 2-SAT 2SAT。总时间复杂度 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;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/115270906
今日推荐