题目
IOI 铁路公司在一条铁轨上运营线路。铁轨为一条直线,该铁轨上有 个车站,编号为 。车站 与车站 之间由一条铁轨直接连接。
IOI 铁路公司正在运营 条线路,编号为 。线路 的起点为 ,终点为 ,列车在每一站均会停靠,即如果 ,一列 号线的列车会按顺序在车站 停靠。如果 ,一列 号线的列车会按顺序在车站 停靠。
JOI 君是一个旅行者。他有 个旅行计划。在第 个旅行计划中他计划从车站 通过乘坐列车前往车站 。
然而,JOI 君因长途跋涉而疲惫不堪。他希望他乘坐的列车均有空座以便休息。因此 JOI 君决定,只有当某条线路的起点站是第 个或更早的车站时,他才会在该站乘坐该条线路的列车。换句话说,对于线路 ,如果 ,那么他只会在车站 乘上线路 的列车。如果 ,那么他只会在车站 乘上线路 的列车。
在这些条件下,JOI 君希望尽量减少乘坐火车的次数。
给出 IOI 铁路公司的线路信息和 JOI 君的计划,写一个程序计算对于 JOI 君的每一个计划,他所需的最小乘车次数。
对于 的数据,,,,,,,,(),()。
题解
为了学习算法写的这道题,抄的题解
暴力做法
从起点开始深搜,复杂度爆炸
优化一点
设f[i][j]
表示从i
点走j
次路线所能到达的最远的地方(pair,左右端点)
预处理出所有的f[i][j]
,枚举f[i][j - 1]
中的所有点k,看f[k][1]
能到达的左右最大值
复杂度,会炸
再优化一点
看到这种走走停停的题,考虑能不能倍增
设f[i][j]
表示i
点走次路线所能到达的最远的地方(pair,左右端点)
考虑转移,枚举f[i][j - 1]
的每个位置k
,找到f[k][j - 1]
能到达的左右最大值
显然不能暴力枚举找最大值,可以对于每一个2^j
代表的j
建立一个st表,(因为没有修改),然后可以做了
结果查询怎么办呢
很像lca,枚举f[起点][w]
,w
从大到小枚举,如果到得了终点
,那么w继续减,直到到不了为止。最后如果结束的点没法到终点,输出-1,否则res+1即可
锅
st表写炸了。。。。
代码
#include<bits/stdc++.h>#define PII pair<int, int>#define l first#define r secondusing 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];//0代表fl,1代表fr
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;// cout << k << " " << L << " " << R << endl; 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;}