本文搬运自本人初中博客园博客,若图片加载不出来,可到原文查看:https://www.cnblogs.com/zhangtingxi/p/15901652.html
题目
原题来自:POI 2004
John 打算驾驶一辆汽车周游一个环形公路。公路上总共有 n n n 车站,每站都有若干升汽油(有的站可能油量为零),每升油可以让汽车行驶一千米。John 必须从某个车站出发,一直按顺时针(或逆时针)方向走遍所有的车站,并回到起点。在一开始的时候,汽车内油量为零,John 每到一个车站就把该站所有的油都带上(起点站亦是如此),行驶过程中不能出现没有油的情况。
任务:判断以每个车站为起点能否按条件成功周游一周。
思路
环形问题难搞,考虑破环成链。
破成一个长度为 2 n 2n 2 n 的车站序列,让我们先考虑顺时针的情况。
让我们对汽车油量和距离分别做前缀和 s 1 , s 2 s1, s2 s 1 , s 2 ,如果从第 i i i 个点出发可以回到起点,就意味着在 [ i , i + n − 1 ] [i, i+n-1] [ i , i + n − 1 ] 的区间内,满足所有油量与距离之差的前缀和都大于等于0,即满足 ( s 1 j − s 1 i − 1 ) − ( s 2 j − s 2 i − 1 ) ⩾ 0 ( j ∈ [ i , i + n − 1 ] ) (s1_j-s1_{i-1})-(s2_j-s2_{i-1})\geqslant 0\,\,\,(j\in [i, i+n-1]) ( s 1 j − s 1 i − 1 ) − ( s 2 j − s 2 i − 1 ) ⩾ 0 ( j ∈ [ i , i + n − 1 ]) 。
可是这样子不好维护,于是我们可以尝试对汽车油量与距离之差做前缀和,记为 s s s 。
有了 s s s 数组,我们就可以求区间最小值减去 s i − 1 s_{i-1} s i − 1 与0作比较,相当于做一个长度为 n n n 的滑动窗口。
逆时针同理。
总结
这道题的思路挺巧妙的。
首先对于这类题,可以先考虑破环成链,这是这类题目的常见思考方向。
然后,我们对于题目的本质进行分析,然后可以想出差值前缀和。
最后,我们发现求最小值的过程中可以使用数据结构,即单调队列进行优化,然后就能做出此题。
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 #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 N 1000010 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 () { 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<=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 ]; 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<=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 ]); 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 ; }