zoukankan      html  css  js  c++  java
  • BZOJ1997 : [HNOI2010] Planar (2-SAT)

    题意

    给你一个有 (n) 个点的图 里面有 (m) 条边

    并给你图上一条哈密顿回路 然后让你判断它是否为平面图

    平面图就是存在一种情况 使得边两两不相交 数据共 (T)

    [(T le 100 , 3 le n le 200, m le 10000) ]

    题解

    这题是我见过最不裸的 (2-SAT) 了 根本不会啊

    判断平面图 有一些鬼畜的算法可以做 但根本不会啊

    这题比较特殊 给了一条哈密顿回路 那么我们可以利用这个性质

    不难发现 如果两条边会相交 那么他们不能同时存在 在 哈密顿圈 内 或者 外面

    这样 我们对于原图中每条边就变成 (2-SAT) 上的点然后去判断就行了

    至于判边相交 考虑如下一种情况

    pic

    我们将这个环重新标号 标为 (1 o 6)

    然后如果两条边 (设为 (u_i o v_i)(u_j o v_j) 强制 (u_i < u_j , u_i < v_i , u_j < v_j) ) 会相交 那么我们就有一个不等式

    [u_i < u_j < v_i < v_j ]

    所有相交情况都可以这样转化

    但数据组数多 边的范围原超过点的范围 可能会TLE

    所以我们就可以用个性质进行一些优化

    平面图上 边数 (E) 和 点数 (V) 满足 (E le V * 3 - 6)

    这个一开始直接判就行了qwq

    代码

    有一些鬼畜的地方 就是点和边的范围 以及 他们一开始清空的范围

    /**************************************************************
        Problem: 1997
        User: zjp_shadow
        Language: C++
        Result: Accepted
        Time:180 ms
        Memory:10440 kb
    ****************************************************************/
     
    #include <bits/stdc++.h>
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    using namespace std;
     
    inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
     
    inline int read() {
        int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
        return x * fh;
    }
     
    void File() {
    #ifdef zjp_shadow
        freopen ("1997.in", "r", stdin);
        freopen ("1997.out", "w", stdout);
    #endif
    }
     
    const int N = 110000 << 1, M = 100100 << 1;
     
    struct Two_SAT {
    #define Travel(i, u, v) for(int i = Head[u], v = to[i]; i; i = Next[i], v = to[i])
        int Head[N], Next[M], to[M], e, n;
     
        void add_edge(int u, int v) { to[++ e] = v; Next[e] = Head[u]; Head[u] = e; }
     
        void Add(int x, int xv, int y, int yv) {
            x = x << 1 | xv;
            y = y << 1 | yv;
            add_edge(x, y);
            add_edge(y ^ 1, x ^ 1);
        }
     
        void Init(int n) {
            this -> n = n; e = 0; 
            For (i, 1, n * 2 + 10) Head[i] = 0;
        }
     
        int sccno[N], scc_cnt;
        int clk, dfn[N], lowlink[N];
        int sta[N], top;
        void Tarjan(int u) {
            lowlink[u] = dfn[u] = ++ clk; sta[++ top] = u;
            Travel(i, u, v)
                if (!dfn[v]) Tarjan(v), chkmin(lowlink[u], lowlink[v]);
                else if (!sccno[v]) chkmin(lowlink[u], dfn[v]);
            if (lowlink[u] == dfn[u]) {
                ++ scc_cnt;
                for (;;) {
                    int x = sta[top --];
                    sccno[x] = scc_cnt;
                    if (x == u) break;
                }
            }
        }
     
        bool Solve() {
            For (i, 1, n * 2 + 10)
                dfn[i] = sccno[i] = lowlink[i] = 0;
            scc_cnt = clk = top = 0;
            For (i, 1, n * 2 + 1)
                if (!dfn[i]) Tarjan(i);
            For (i, 1, n)
                if (sccno[i << 1] == sccno[i << 1 | 1]) return false;
            return true;
        }
    } T;
     
    struct Edge{
        int u, v;
        inline bool operator < (const Edge &rhs) const {
            return (u ^ rhs.u) ? u < rhs.u : v < rhs.v;
        }
    } lt[M];
     
    int ver[N], num[N];
     
    int main () {
        File();
        int cases = read();
        while (cases --) {
            int n = read(), m = read();
            For (i, 1, m) {
                lt[i].u = read();
                lt[i].v = read();
            }
            For (i, 1, n) {
                ver[i] = read();
                num[ver[i]] = i;
            }
            if (m > 3 * n - 6) { puts("NO"); continue; }
            For (i, 1, m) {
                lt[i].u = num[lt[i].u];
                lt[i].v = num[lt[i].v];
                if (lt[i].u > lt[i].v) swap(lt[i].u, lt[i].v);
            }
            sort(lt + 1, lt + 1 + m);
            T.Init(n);
            For (i, 1, m) For (j, i + 1, m) {
                if (lt[i].u == lt[i].v - 1 || (lt[i].u == 1 && lt[i].v == n)) continue ;
                if (lt[i].u < lt[j].u && lt[j].u < lt[i].v && lt[i].v < lt[j].v) {
                    T.Add(i, 1, j, 0);
                    T.Add(i, 0, j, 1);
                }
            }
            puts(T.Solve() ? "YES" : "NO");
        }
        return 0;
    }
    
  • 相关阅读:
    再见OI,AFO
    时间复杂度
    NOIP真题:矩阵取数问题
    [USACO12FEB]附近的牛Nearby Cows
    合唱队
    子串
    ZJOI2010基站选址
    分治FFT学习笔记
    「HAOI2018」染色
    「SDOI2015」序列统计
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/8721593.html
Copyright © 2011-2022 走看看