题解 P3209 【[HNOI2010]PLANAR】

xyz32768

2017-11-28 21:23:53

Solution

首先,平面图的性质:边数小于等于$3n-6$。不符合的直接跳过。 然后可以发现,对于哈密尔顿环之外的任意一条边,要么连在环内部,要么连在环外部。在一定的条件下(可以简单判断),如果两条边**同时**连在环内部或**同时**连在环外部,这两条边就一定会相交,这样的限制条件符合2-SAT的模型。 把第$i$条边拆成$i$和$i'$,$i$表示这条边连在内部,$i'$表示连在外部。 对于任意两条不在哈密尔顿环上的边$i,j,i\neq j$,如果他们不能同时连在环内或环外,则: 1、建边$<i,j'>$,表示$i$在内则$j$必须在外。 2、建边$<i',j>$,表示$i$在外则$j$必须在内。 3、建边$<j,i'>$,表示$j$在内则$i$必须在外。 4、建边$<j',i>$,表示$j$在外则$i$必须在内。 然后求一遍强连通分量,如果存在一个$i$和$i'$在同一个强连通分量里,那么原图不是平面图,否则是平面图。 代码: ```cpp #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; inline int read() { int res = 0; bool bo = 0; char c; while (((c = getchar()) < '0' || c > '9') && c != '-'); if (c == '-') bo = 1; else res = c - 48; while ((c = getchar()) >= '0' && c <= '9') res = (res << 3) + (res << 1) + (c - 48); return bo ? ~res + 1 : res; } const int V = 205, N = 3e4 + 5, M = 2e6 + 5; int n, m, eX[N], eY[N], Cir[V], ecnt, nxt[M], adj[N], go[M], dfn[N], low[N], top, times, stk[N], bel[N], sum, rev[V], Ex[N], Ey[N]; bool cir[V][V], ins[N]; void add_edge(int u, int v) { nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; } void Tarjan(int u) { dfn[u] = low[u] = ++times; stk[++top] = u; ins[u] = 1; for (int e = adj[u], v; e; e = nxt[e]) if (!dfn[v = go[e]]) { Tarjan(v); low[u] = min(low[u], low[v]); } else if (ins[v]) low[u] = min(low[u], dfn[v]); if (dfn[u] == low[u]) { int v; bel[u] = ++sum; ins[u] = 0; while (v = stk[top--], v != u) bel[v] = sum, ins[v] = 0; } } bool check() { int i; for (i = 1; i <= (m << 1); i++) if (!dfn[i]) Tarjan(i); for (i = 1; i <= m; i++) if (bel[i] == bel[i + m]) return 0; return 1; } void work() { ecnt = times = sum = 0; memset(adj, 0, sizeof(adj)); memset(dfn, 0, sizeof(dfn)); memset(bel, 0, sizeof(bel)); memset(low, 0, sizeof(low)); memset(cir, 0, sizeof(cir)); int i, j, u, v, x, y, tot = 0; n = read(); m = read(); for (i = 1; i <= m; i++) { eX[i] = read(); eY[i] = read(); if (eX[i] > eY[i]) swap(eX[i], eY[i]); } for (i = 1; i <= n; i++) { rev[Cir[i] = read()] = i; if (i > 1) { x = Cir[i - 1]; y = Cir[i]; if (x < y) cir[x][y] = 1; else cir[y][x] = 1; } } if (m > 3 * n - 6) return (void) (printf("NO\n")); x = Cir[n]; y = Cir[1]; ((x < y) ? cir[x][y] : cir[y][x]) = 1; for (i = 1; i <= m; i++) { if (cir[eX[i]][eY[i]]) continue; Ex[++tot] = eX[i]; Ey[tot] = eY[i]; } m = tot; for (i = 1; i < m; i++) for (j = i + 1; j <= m; j++) { u = rev[Ex[i]], v = rev[Ey[i]], x = rev[Ex[j]], y = rev[Ey[j]]; if (u > v) swap(u, v); if (x > y) swap(x, y); if ((u < x && v > x && v < y) || (u > x && u < y && v > y)) { add_edge(i, j + m); add_edge(i + m, j); add_edge(j, i + m); add_edge(j + m, i); } } printf(check() ? "YES\n" : "NO\n"); } int main() { int T = read(); while (T--) work(); return 0; } ```