题目
IOI 铁路公司在一条铁轨上运营线路。铁轨为一条直线,该铁轨上有 N N N 个车站,编号为 1 ∼ N 1 \sim N 1 ∼ N 。车站 i i i 与车站 i + 1 i + 1 i + 1 之间由一条铁轨直接连接。
IOI 铁路公司正在运营 M M M 条线路,编号为 1 ∼ M 1 \sim M 1 ∼ M 。线路 j j j 的起点为 A j A_j A j ,终点为 B j B_j B j ,列车在每一站均会停靠,即如果 A j < B j A_j < B_j A j < B j ,一列 j j j 号线的列车会按顺序在车站 A j , A j + 1 , … , B j A_j, A_j + 1, \ldots, B_j A j , A j + 1 , … , B j 停靠。如果 A j > B j A_j > B_j A j > B j ,一列 j j j 号线的列车会按顺序在车站 A j , A j − 1 , … , B j A_j, A_j - 1, \ldots, B_j A j , A j − 1 , … , B j 停靠。
JOI 君是一个旅行者。他有 Q Q Q 个旅行计划。在第 k k k 个旅行计划中他计划从车站 S k S_k S k 通过乘坐列车前往车站 T k T_k T k 。
然而,JOI 君因长途跋涉而疲惫不堪。他希望他乘坐的列车均有空座以便休息。因此 JOI 君决定,只有当某条线路的起点站是第 K K K 个或更早的车站时,他才会在该站乘坐该条线路的列车。换句话说,对于线路 j j j ,如果 A j < B j A_j < B_j A j < B j ,那么他只会在车站 A j , A j + 1 , … , min { A j + K − 1 , B j − 1 } A_j, A_j + 1, \ldots, \min \{ A_j + K - 1, B_j - 1 \} A j , A j + 1 , … , min { A j + K − 1 , B j − 1 } 乘上线路 j j j 的列车。如果 A j > B j A_j > B_j A j > B j ,那么他只会在车站 A j , A j − 1 , … , max { A j − K + 1 , B j + 1 } A_j, A_j - 1, \ldots, \max \{ A_j - K + 1, B_j + 1 \} A j , A j − 1 , … , max { A j − K + 1 , B j + 1 } 乘上线路 j j j 的列车。
在这些条件下,JOI 君希望尽量减少乘坐火车的次数。
给出 IOI 铁路公司的线路信息和 JOI 君的计划,写一个程序计算对于 JOI 君的每一个计划,他所需的最小乘车次数。
对于 100 % 100 \% 1 0 0 % 的数据,2 ≤ N ≤ 10 5 2 \le N \le {10}^5 2 ≤ N ≤ 1 0 5 ,1 ≤ K ≤ N − 1 1 \le K \le N - 1 1 ≤ K ≤ N − 1 ,1 ≤ M ≤ 2 × 10 5 1 \le M \le 2 \times {10}^5 1 ≤ M ≤ 2 × 1 0 5 ,1 ≤ Q ≤ 5 × 10 4 1 \le Q \le 5 \times {10}^4 1 ≤ Q ≤ 5 × 1 0 4 ,1 ≤ A j , B j , S k , T k ≤ N 1 \le A_j, B_j, S_k, T_k \le N 1 ≤ A j , B j , S k , T k ≤ N ,A j ≠ B j A_j \ne B_j A j = B j ,S k ≠ T k S_k \ne T_k S k = T k ,( A j , B j ) ≠ ( A k , B k ) (A_j, B_j) \ne (A_k, B_k) ( A j , B j ) = ( A k , B k ) (j ≠ k j \ne k j = k ),( S k , T k ) ≠ ( S l , T l ) (S_k, T_k) \ne (S_l, T_l) ( S k , T k ) = ( S l , T l ) (k ≠ l k \ne l k = l )。
题解
为了学习算法写的这道题,抄的题解
暴力做法
从起点开始深搜,复杂度爆炸
优化一点
设f[i][j]
表示从i
点走j
次路线所能到达的最远的地方(pair,左右端点)
预处理出所有的f[i][j]
,枚举f[i][j - 1]
中的所有点k,看f[k][1]
能到达的左右最大值
复杂度O ( n 2 m ) O(n^2m) O ( n 2 m ) ,会炸
再优化一点
看到这种走走停停 的题,考虑能不能倍增
设f[i][j]
表示i
点走2 j 2^j 2 j 次路线所能到达的最远的地方(pair,左右端点)
考虑转移,枚举f[i][j - 1]
的每个位置k
,找到f[k][j - 1]
能到达的左右最大值
显然不能暴力枚举找最大值,可以对于每一个2^j
代表的j
建立一个st表,(因为没有修改),然后可以做了
结果查询怎么办呢
很像lca,枚举f[起点][w]
,w
从大到小枚举,如果到得了终点
,那么w继续减,直到到不了为止。最后如果结束的点没法到终点,输出-1,否则res+1即可
锅
st表写炸了。。。。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 #include <bits/stdc++.h> #define PII pair<int, int> #define l first #define r second using namespace std;const int N = 1e5 + 10 ;const int M = 2e5 + 10 ;PII up_route[M], down_route[M]; int upcnt, downcnt;int fl[N][20 ], fr[N][20 ];int n, m, k;int qwq[M], hh = 1 , tt = 0 ;bool cmp (PII A, PII B) { return A.second > B.second; } const int LOG = 20 ;struct ST { int st[N][LOG]; void init (int k,int lorr) { if (lorr == 0 ) { memset (st, 0x3f , sizeof st); } for (int i = 1 ;i <= n;i++) st[i][0 ] = ((!lorr) ? (fl[i][k]) : (fr[i][k])); } void prepare (int lorr) { for (int j = 1 ;j < LOG;j++) { for (int i = 1 ;i + (1 << j) - 1 <= n;i++) { st[i][j] = ((!lorr) ? min (st[i][j - 1 ], st[i + (1 << (j - 1 ))][j - 1 ]) : max (st[i][j - 1 ], st[i + (1 << (j - 1 ))][j - 1 ])); } } } int query (int l, int r, int lorr) { int lllooojjj = log2 (r - l + 1 ); return ((!lorr) ? min (st[l][lllooojjj], st[r - (1 << lllooojjj) + 1 ][lllooojjj]) : max (st[l][lllooojjj], st[r - (1 << lllooojjj) + 1 ][lllooojjj])); } }s[20 ][2 ]; int ask (int b, int t) { int res = 0 ; int l = b, r = b; for (int k = 19 ;k >= 0 ;k--) { int L = s[k][0 ].query (l, r, 0 ); int R = s[k][1 ].query (l, r, 1 ); if (L <= t && t <= R) continue ; res += (1 << k); l = L, r = R; } int L = s[0 ][0 ].query (l, r, 0 ); int R = s[0 ][1 ].query (l, r, 1 ); if (L <= t && t <= R) return res + 1 ; return -1 ; } int main () { ios::sync_with_stdio (0 ), cin.tie (0 ), cout.tie (0 ); cin >> n >> k; cin >> m; int u, v; for (int i = 1 ;i <= m;i++) { cin >> u >> v; if (u < v) { up_route[++upcnt] = make_pair (u, v); } else { down_route[++downcnt] = make_pair (v, u); } } if (upcnt >= 1 ) sort (up_route + 1 , up_route + upcnt + 1 ); if (downcnt >= 1 ) sort (down_route + 1 , down_route + downcnt + 1 , cmp); for (int i = 1 ;i <= n;i++) fl[i][0 ] = fr[i][0 ] = i; if (upcnt >= 1 ) for (int i = 1 , j = 1 ;i <= n;i++) { while (j <= upcnt && up_route[j].l <= i) { while (hh <= tt && up_route[j].r >= up_route[qwq[tt]].r) { tt --; } qwq[++tt] = j; j++; } while (hh <= tt && up_route[qwq[hh]].l + k - 1 < i) { hh++; } if (hh > tt) continue ; fr[i][0 ] = max (fr[i][0 ], up_route[qwq[hh]].r); } memset (qwq, 0 , sizeof qwq); hh = 1 , tt = 0 ; if (downcnt >= 1 ) for (int i = n, j = 1 ;i >= 1 ;i--) { while (j <= downcnt && down_route[j].r >= i) { while (hh <= tt && down_route[j].l <= down_route[qwq[tt]].l) { tt --; } qwq[++tt] = j; j++; } while (hh <= tt && down_route[qwq[hh]].r - k + 1 > i) { hh++; } if (hh > tt) continue ; fl[i][0 ] = min (fl[i][0 ], down_route[qwq[hh]].l); } s[0 ][0 ].init (0 , 0 ); s[0 ][0 ].prepare (0 ); s[0 ][1 ].init (0 , 1 ); s[0 ][1 ].prepare (1 ); for (int k = 1 ;k < 20 ;k++) { for (int i = 1 ;i <= n;i++) { fl[i][k] = s[k - 1 ][0 ].query (fl[i][k - 1 ], fr[i][k - 1 ], 0 ); fr[i][k] = s[k - 1 ][1 ].query (fl[i][k - 1 ], fr[i][k - 1 ], 1 ); } s[k][0 ].init (k, 0 ); s[k][0 ].prepare (0 ); s[k][1 ].init (k, 1 ); s[k][1 ].prepare (1 ); } int Q; cin >> Q; int s, t; while (Q -- ) { cin >> s >> t; cout << ask (s, t) << "\n" ; } return 0 ; }