本文搬运自本人初中博客园博客,若图片加载不出来,可到原文查看:https://www.cnblogs.com/zhangtingxi/p/15901652.html

题目链接

题目

原题来自:POI 2004

John 打算驾驶一辆汽车周游一个环形公路。公路上总共有 nn 车站,每站都有若干升汽油(有的站可能油量为零),每升油可以让汽车行驶一千米。John 必须从某个车站出发,一直按顺时针(或逆时针)方向走遍所有的车站,并回到起点。在一开始的时候,汽车内油量为零,John 每到一个车站就把该站所有的油都带上(起点站亦是如此),行驶过程中不能出现没有油的情况。

任务:判断以每个车站为起点能否按条件成功周游一周。

思路

环形问题难搞,考虑破环成链。

破成一个长度为 2n2n 的车站序列,让我们先考虑顺时针的情况。

让我们对汽车油量和距离分别做前缀和 s1,s2s1, s2,如果从第 ii 个点出发可以回到起点,就意味着在 [i,i+n1][i, i+n-1] 的区间内,满足所有油量与距离之差的前缀和都大于等于0,即满足 (s1js1i1)(s2js2i1)0(j[i,i+n1])(s1_j-s1_{i-1})-(s2_j-s2_{i-1})\geqslant 0\,\,\,(j\in [i, i+n-1])

可是这样子不好维护,于是我们可以尝试对汽车油量与距离之差做前缀和,记为 ss

有了 ss 数组,我们就可以求区间最小值减去 si1s_{i-1} 与0作比较,相当于做一个长度为 nn 的滑动窗口。

逆时针同理。

总结

这道题的思路挺巧妙的。

首先对于这类题,可以先考虑破环成链,这是这类题目的常见思考方向。

然后,我们对于题目的本质进行分析,然后可以想出差值前缀和。

最后,我们发现求最小值的过程中可以使用数据结构,即单调队列进行优化,然后就能做出此题。

Code

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
// Problem: 1600锛氥€愪緥 4銆戞梾琛岄棶棰?
// Contest: SSOIER
// URL: http://ybt.ssoier.cn:8088/problem_show.php?pid=1600
// Memory Limit: 524 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define mo
#define N 1000010
//#define M
int n, m, i, j, k;
int o[N], d[N], s[N<<1], ans[N];
pair<int, int>p;
deque<pair<int, int> >q;

signed main()
{
// freopen("tiaoshi.in","r",stdin);
// freopen("tiaoshi.out","w",stdout);
n=read();
for(i=1; i<=n; ++i) o[i]=read(), d[i]=read();
for(i=1; i<=2*n; ++i) s[i]=s[i-1]+o[(i-1)%n+1]-d[(i-1)%n+1];
// for(i=1; i<=2*n; ++i) printf("%d ", s[i]); printf("\n");
for(i=1; i<=n; ++i)
{
while(!q.empty() && q.back().first>s[i]) q.pop_back();
p.first=s[i]; p.second=i; q.push_back(p);
}
for(i=n+1; i<=2*n; ++i)
{
ans[i-n]=q.front().first-s[i-n-1];
// printf("ans[%lld]=%lld-%lld=%lld\n", i-n, q.front().first,
// s[i-n-1], q.front().first-s[i-n-1]);
while(!q.empty() && i-q.front().second+1>n) q.pop_front();
while(!q.empty() && q.back().first>s[i]) q.pop_back();
p.first=s[i]; p.second=i; q.push_back(p);
}
q.clear();
for(i=1, j=2*n; i<=2*n; ++i, --j) s[i]=s[i-1]+o[(j-1)%n+1]-d[((j-1>0 ? j-1 : n)-1)%n+1];
// for(i=1; i<=2*n; ++i) printf("%d ", s[i]); printf("\n");
for(i=1; i<=n; ++i)
{
while(!q.empty() && q.back().first>s[i]) q.pop_back();
p.first=s[i]; p.second=i; q.push_back(p);
}
for(i=n+1; i<=2*n; ++i)
{




ans[2*n-i+1]=max(ans[2*n-i+1], q.front().first-s[i-n-1]);
// ans[i-n]=q.front().first-s[i-n-1];
// printf("ans[%lld]=%lld-%lld\n", i-n, q.front().first, s[i-n-1]);
// printf("ans[%lld]=%lld-%lld=%lld\n", 2*n-i+1, q.front().first,
// s[i-n-1], q.front().first-s[i-n-1]);


while(!q.empty() && i-q.front().second+1>n) q.pop_front();
while(!q.empty() && q.back().first>s[i]) q.pop_back();
p.first=s[i]; p.second=i; q.push_back(p);
}
for(i=1; i<=n; ++i) printf(ans[i]>=0 ? "TAK\n" : "NIE\n");
return 0;
}