AcWing 1088 旅行问题
题目传送门
参考:
https://www.acwing.com/solution/content/12142/
https://www.acwing.com/solution/content/34005/
顺时针
每个点i
表示从i
点加oil[i]
的油再耗dist[i]
的油所剩的油量,即oil[i] - dist[i]
-
计算出油量的前缀和
-
从某个点
i
出发,顺时针走一圈,在过程中油量始终>= 0
,等价于在[i,i + n - 1]
中,对任意的j,i <= j <= i + n - 1
,均有s[j] - s[i - 1] >= 0
,即i
固定,找s[j]
的最小值,即从[i,i + n - 1]
中找到滑动窗口的最小值 -
由于
2
操作,需要对i
进行反向遍历,即从n * 2
遍历到1
,又由于i <= j <= i + n - 1
,遍历到i
时需要用到i
位置的值,因此找[i,i + n - 1]
区间最小值时需要在while
后面的语句找
逆时针
每个点i
表示从i
点加oil[i]
的油再耗dist[i - 1]
的油所剩的油量,即oil[i] - dist[i - 1]
,其中1
号点耗的是dist[n]
的油,因此需要初始化dist[0] = dist[n]
-
计算出油量的后缀和
-
从某个点
i
出发,逆时针走一圈,在过程中油量始终>= 0
,等价于在[i - n + 1,i]
中,对任意的j,i - n + 1 <= j <= i
,均有s[j] - s[i + 1] >= 0
,即i
固定,找s[j]
的最小值,即从[i - n + 1,i]
中找到滑动窗口的最小值 -
由于
2
操作,需要对i
进行正向遍历,即从1
遍历到n * 2
,又由于i - n + 1 <= j <= i
,遍历到i
时需要用到i
位置的值,因此找[i - n + 1,i]区间最小值时需要在
while`后面的语句找
#include
using namespace std;
typedef long long LL;
const int N = 2000010;
int n;
int oil[N], dist[N];
LL s[N]; // 顺时针时表示oil[i]-dist[i]的前缀和,逆时针时表示oil[i]-dist[i-1]的后缀和
int q[N]; // 单调队列维护长度为n的区间中s[i]的最小值
bool st[N]; // st[i]为true表示从i开始有解
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> oil[i] >> dist[i];
// 顺时针时求oil[i]-dist[i]的前缀和,这个可以通过画环形图进行深入理解
for (int i = 1; i <= n; i++) s[i] = s[i + n] = oil[i] - dist[i];
for (int i = 1; i <= n * 2; i++) s[i] += s[i - 1];
// 从大到小枚举i,然后考虑区间[i,i+n-1]中的最小值是否>=s[i-1]
int hh = 0, tt = -1;
//顺时针需要求出 后面 一段区间中的最值,只有从后往前做才能在处理到当前数的时候,
//把后面数的信息存下来。
for (int i = n * 2; i; i--) {
// 判断是否滑出[i,i+n-1]的窗口
if (hh <= tt && q[hh] > i + n - 1) hh++;
while (hh <= tt && s[q[tt]] >= s[i]) tt--;
q[++tt] = i;
// 此时队头元素就是区间min
// 表示以i起起始顺时针走有解
if (i <= n && s[q[hh]] - s[i - 1] >= 0) st[i] = true;//如果从i点出发,那么全程中
}
// 逆时针时求oil[i]-dist[i-1]的后缀和
dist[0] = dist[n]; // oil[1]-dist[0]时需要用到
for (int i = n; i; i--) s[i] = s[i + n] = oil[i] - dist[i - 1];//画图理解
for (int i = n * 2; i; i--) s[i] += s[i + 1];//后缀和
// 从小到大枚举i,然后考虑区间[i-n+1,i]中的最小值是否>=s[i+1]
hh = 0, tt = -1;
for (int i = 1; i <= n * 2; i++) {
if (hh <= tt && q[hh] < i - n + 1) hh++;
while (hh <= tt && s[q[tt]] >= s[i]) tt--;
q[++tt] = i;
// 注意这里起始位置是i-n+1的前一个位置
if (i > n && s[q[hh]] - s[i + 1] >= 0) st[i - n] = true;
}
//输出
for (int i = 1; i <= n; i++) puts(st[i] ? "TAK" : "NIE");
return 0;
}