HNOI2010 平面图判定

感觉 (2-SAT) 的题目跟网络流有点点像,都是考虑怎么建图,然后跑板子

(2-SAT) 的题目的一个建图的思路就是转换成一真一假的格式,然后就比较好

Description

给一张图和一条图上的哈密尔顿回路,判定是不是可以把它转换成一个平面图

(n leq 200),(m leq 10000)

多组数据((doc)我多测没清空,(WA)了好几发)

Solution

先是一个实用 (trick) :平面图中,(m leq 3n-6)

证明等学平面图的时候再说吧

把这一个哈密尔顿回路想成平面上的一个圆(多边形)

而非回路上的点就是圆上的弦(多边形对角线),或者连在环外部

两条弦相交的条件还得满足

for (int i = 1; i < m; ++i) {
        for (int j = i + 1; j <= m; ++j) {
            int p = id[ex[i]], q = id[ey[i]], r = id[ex[j]], s = id[ey[j]];
            if (p > q)
                swap(p, q);
            if (r > s)
                swap(r, s);
            if ((p < r && q > r && q < s || (r < p && s > p && s < q)))
                add(i, j + m), add(i + m, j), add(j, i + m), add(j + m, i);
        }
    }

反正博主是懂了(画图就可以感性理解)

然后跑 (tarjan) 就没了

Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm {
inline int read() {
    int res = 0, f = 1;
    char k;
    while (!isdigit(k = getchar()))
        if (k == '-')
            f = -1;
    while (isdigit(k)) res = res * 10 + k - '0', k = getchar();
    return res * f;
}
const int N = 1e4 + 10;
int dfn[N], low[N], scc[N], st[N], tot, tim, top, u[N], v[N], c[N], ex[N], ey[N], now;
vector<int> g[N];
int n, id[N], m;
bool f[300][300], vis[N];
inline void add(int u, int v) { return g[u].push_back(v), void(); }
inline void tarjan(int x) {
    dfn[x] = low[x] = ++tim;
    vis[x] = 1;
    st[++top] = x;
    int sz = g[x].size();
    for (int i = 0; i < sz; ++i) {
        int t = g[x][i];
        if (!dfn[t])
            tarjan(t), low[x] = min(low[x], low[t]);
        else if (vis[t])
            low[x] = min(low[x], dfn[t]);
    }
    if (dfn[x] == low[x]) {
        ++tot;
        while (st[top] != x) scc[st[top]] = tot, vis[st[top--]] = 0;
        scc[st[top]] = tot, vis[st[top--]] = 0;
    }
    return;
}
#define cl(x) memset(x, 0, sizeof(x))
inline void prework() {
    cl(dfn);
    cl(scc);
    cl(st);
    cl(u);
    cl(v);
    cl(ex);
    now = 0;
    tim = 0;
    tot = 0;
    top = 0;
    cl(ey);
    cl(id);
    cl(vis);
    cl(f);
    for (int i = 1; i < N; ++i) g[i].clear();
    return;
}
inline void work() {
    n = read();
    m = read();
    for (int i = 1; i <= m; ++i) {
        u[i] = read();
        v[i] = read();
        if (u[i] > v[i])
            swap(u[i], v[i]);
    }
    for (int i = 1, x, y; i <= n; ++i) {
        c[i] = read();
        id[c[i]] = i;
        if (i > 1) {
            x = c[i];
            y = c[i - 1];
            f[min(x, y)][max(x, y)] = 1;
        }
    }
    if (m > 3 * n - 6)
        return puts("NO"), void();
    f[min(c[1], c[n])][max(c[1], c[n])] = 1;
    for (int i = 1; i <= m; ++i) {
        if (f[u[i]][v[i]])
            continue;
        ex[++now] = u[i], ey[now] = v[i];
    }
    //把非回路上的点找出来
    m = now;
    for (int i = 1; i < m; ++i) {
        for (int j = i + 1; j <= m; ++j) {
            int p = id[ex[i]], q = id[ey[i]], r = id[ex[j]], s = id[ey[j]];
            if (p > q)
                swap(p, q);
            if (r > s)
                swap(r, s);
            if ((p < r && q > r && q < s || (r < p && s > p && s < q)))
                add(i, j + m), add(i + m, j), add(j, i + m), add(j + m, i);
        }
    }
    for (int i = 1; i <= m * 2; ++i)
        if (!dfn[i])
            tarjan(i);
    for (int i = 1; i <= m; ++i)
        if (scc[i] == scc[i + m])
            return puts("NO"), void();
    puts("YES");
    return;
}
signed main() {
    int T = read();
    while (T--) prework(), work();
    return 0;
}
}  // namespace yspm
signed main() { return yspm::main(); }
原文地址:https://www.cnblogs.com/yspm/p/12324678.html