AT2534 港湾設備 (Port Facility)

题目链接

主席树好难调,简要记录以下正解:

显然可以看成连边跑黑白染色,但是边数太多没法连。

于是可以用主席树优化建图

我们先考虑用一个栈来记录还没扫到右端点的左端点。这样的话当前如果扫到一个右端点,找到其对应左端点后栈内从左端点到最后都是和它有交叉的线段,都需要连边,然后将当前左端点删除,删除方法用并查集(类似LCT的缩点)。发现那个线段一定会被染成相同的染色,否则无解,于是我们这次连以后再遇到这些点只需再连其中一个(随意“缩点”)即可,这个也可以用并查集或者直接记一个 nxt 数组即可。

关键代码:

int col[N];
void dfs(int cur) {
  for (int i = head[cur]; i; i = e[i].nxt) {
    int to = e[i].to;
    if (col[to] == -1)  col[to] = col[cur] ^ 1, dfs(to);
    else if (col[to] == col[cur]) { puts("0"); exit(0); }
  }
}
int main() {
  read(n);
  for (int i = 1; i <= n; ++i) {
    int x, y; read(x), read(y);
    h[x] = i; h[y] = i;
  }
  for (int i = 1; i <= n + n; ++i)  fa[i] = i, nxt[i] = i + 1;
  for (int i = 1; i <= n + n; ++i) {
    int nw = h[i];
    int l = pos[nw];
    if (!l) { stk[++stop] = nw; pos[nw] = stop; continue; }
    fa[l] = l + 1;
    for (int j = find(fa[l]), tmp; j <= stop; tmp = j, j = find(nxt[j]), nxt[tmp] = stop + 1)
      addedge(stk[j], nw), addedge(nw, stk[j]);
  }
  memset(col, -1, sizeof(col));
  int ans = 1;
  for (int i = 1; i <= n; ++i) if (col[i] == -1)
    col[i] = 0, dfs(i), ans = (ans << 1) % P;
  printf("%d
", ans);
  return 0;
}
原文地址:https://www.cnblogs.com/JiaZP/p/13683125.html