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\n", ans);
return 0;
}