[BZOJ3495]PA2010 Riddle[2-SAT]

对于一个国家内部,每个点都要向剩下的所有点连边,会有\(O(n^2)\)条边

学习了前缀和优化建图的新姿势 在每个点拆成两个的基础上,再开两个点 \(i_3\),\(i_4\)表示前缀\([1,i]\)中有/没有首都

这个blog讲得比较详细

struct Edge {
  int v, next;
} G[MAXN << 3]; int head[MAXN << 2], sta[MAXN << 2], top, n, m, bel[MAXN << 2], idx, dfn[MAXN << 2], low[MAXN << 2], k, u, v, C, ins[MAXN << 2], tar;
 
inline void add(int u, int v) {
  static int tot = 0;
  G[++tot] = (Edge) {v, head[u]}; head[u] = tot;
}
 
inline int g(int x, int y) {
  return ((x - 1) << 2) + y;
}
 
inline void tarjan(int u) {
  low[u] = dfn[u] = ++idx;
  ins[sta[++top] = u] = 1;
  for (int i = head[u]; i; i = G[i].next) {
    int v = G[i].v;
    if (!dfn[v])
      tarjan(v), chmin(low[u], low[v]);
    else if (ins[v]) chmin(low[u], dfn[v]);
  }
  if (low[u] == dfn[u]) {
    ++tar; int v;
    do {
      v = sta[top--];
      ins[v] = 0;
      bel[v] = tar;
    } while (v ^ u);
  }
}
 
int main() {
#ifdef LOCAL_DEBUG
  // freopen("data.in", "r", stdin), freopen("data.out", "w", stdout);
  Dbg = 1; uint tim1 = clock();
#endif
  in, n, m, k;
  while (m--) {
    in, u, v; 
    add(g(u, 2), g(v, 1)), add(g(v, 2), g(u, 1));
  }
  while (k--) {
    int las = -1, x;
    in, C, las;
    while (--C) {
      in, x;
      add(g(x, 1), g(las, 4)), add(g(las, 3), g(x, 3)), add(g(x, 4), g(las, 4)), add(g(las, 3), g(x, 2));
      las = x;
    }
  }
 
  lop(i,1,n) add(g(i, 1), g(i, 3)), add(g(i, 4), g(i, 2));
  lop(i,1,n<<2) if (!dfn[i]) tarjan(i);
  for(int i = 1; i <= 4 * n; i += 2)
    if (bel[i] == bel[i + 1]) return puts("NIE"), 0;
  puts("TAK");
#ifdef LOCAL_DEBUG
  fprintf(stderr, "\ntime:%.5lfms", (clock() - tim1) / (1.0 * CLOCKS_PER_SEC) * 1000);
#endif
  return 0;
}
posted @ 2018-12-28 15:31  QvvQ  阅读(136)  评论(0编辑  收藏  举报