1 条题解
-
0
题解
思路概述
- 把房间按锁的位置分成若干连续区间:每当门
i有锁,就把序列在i分割成左/右两段。每段[lf, rf]初始只包含本区间的房间。 - 若锁钥匙在左段,则右段要想打开必须先完成左段;这在区间之间形成了一条依赖边。反之亦然。
- 将这些依赖关系构成 DAG 并做拓扑排序,从“无前驱”的区间开始,逐步扩张其可达区间
[L,R](代表可以畅通的房间范围)。当一个区间的依赖全部满足后,solve()通过拓展左右端点使得新的区间覆盖尽量大。 - 最终每个房间属于某个分段
bl[x],区间的可达范围L[bl], R[bl]即可用于回答询问(S, T):若T在bl[S]对应的[L,R]内,则可达,否则不可达。
复杂度
- 建图 + 拓扑排序 + 区间扩张总计
O(n + m);查询只需O(1)判定。
#include <bits/stdc++.h> using namespace std; const int N = 1000001; int n, m, q, qx, qy, cnt, tot, ptt, head[N], du[N], bl[N], x[N], y[N], L[N], R[N], lc[N]; int lf[N], rf[N]; struct dcz { int nex, to; } a[2 * N]; void add(int x, int y) { a[++cnt].nex = head[x]; a[cnt].to = y; head[x] = cnt; } void solve(int x) { L[x] = lf[x], R[x] = rf[x]; while (1) { bool f = 0; ptt++; assert(ptt < 2e8); if (R[x] < n && lc[R[x]] <= R[x] && lc[R[x]] >= L[x]) { f = 1; R[x] = R[bl[R[x] + 1]]; } if (L[x] > 1 && lc[L[x] - 1] <= R[x] && lc[L[x] - 1] >= L[x]) { f = 1; L[x] = L[bl[L[x] - 1]]; } if (!f) return; } } signed main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n >> m >> q; for (int i = 1; i <= m; i++) { cin >> x[i] >> y[i]; lc[x[i]] = y[i]; } ++tot; lf[tot] = 1; for (int i = 1; i <= n; i++) { bl[i] = tot; if (lc[i] && i != n) { rf[tot] = i; tot++; lf[tot] = i + 1; if (lc[i] <= i) { add(tot, tot - 1); du[tot - 1]++; } else { add(tot - 1, tot); du[tot]++; } } } rf[tot] = n; queue<int> qq; for (int i = 1; i <= tot; i++) { if (!du[i]) qq.push(i); } while (qq.size()) { int u = qq.front(); qq.pop(); solve(u); for (int i = head[u]; i; i = a[i].nex) { int v = a[i].to; if (!(--du[v])) qq.push(v); } } for (int i = 1; i <= q; i++) { cin >> qx >> qy; if (L[bl[qx]] <= qy && R[bl[qx]] >= qy) cout << "YES\n"; else cout << "NO\n"; } return 0; } - 把房间按锁的位置分成若干连续区间:每当门
- 1
信息
- ID
- 3409
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 4
- 已通过
- 1
- 上传者