1. 程式人生 > >[一本通 5.5 例 4」旅行問題

[一本通 5.5 例 4」旅行問題

題目描述:

John 打算駕駛一輛汽車周遊一個環形公路。公路上總共有 n 車站,每站都有若干升汽油(有的站可能油量為零),每升油可以讓汽車行駛一千米。John 必須從某個車站出發,一直按順時針(或逆時針)方向走遍所有的車站,並回到起點。在一開始的時候,汽車內油量為零,John 每到一個車站就把該站所有的油都帶上(起點站亦是如此),行駛過程中不能出現沒有油的情況。

任務:判斷以每個車站為起點能否按條件成功周遊一週。

輸入格式:

第一行是一個整數 n,表示環形公路上的車站數;

接下來 n 行,每行兩個整數 pi,di,分別表示表示第 i 號車站的存油量和第 i 號車站到下一站的距離。

輸出格式

輸出共 n 行,如果從第 i 號車站出發,一直按順時針(或逆時針)方向行駛,能夠成功周遊一圈,則在第 i 行輸出 TAK,否則輸出 NIE

輸入樣例

5
3 1
1 2
5 2
0 1
5 4

輸出樣例
TAK
NIE
TAK
NIE
TAK

n<=1e6,long long


思路:
設ai=pi-di,即此站到下一站花費(或增加)的油量
用sum記錄字首和,即從開始到現在總共花費(或增加)的油量
只要每n個聯絡區間內最小值is負數,那麼就肯定無法周遊一週
暴力的時復為O(n^2)
然而我們發現這是一個連續且序號單調遞升的區間
很容易想到用單調佇列維護一下
這樣時復就是O(n)了
注意:
正著周遊一圈可以,反著周遊一圈也可以,需要在判斷一次
上程式碼
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define rep(i,a,b) for(long long i=a;i<=b;i++)
#define
N 2000500 using namespace std; typedef long long ll; deque<pair<ll,ll> > Q; ll n,p[N],d[N],p1[N],d1[N],a[N],sum[N],ans[N]; ll work(ll x){ if(x==n) return 1; return 2*n-x+1; } int main() { scanf("%lld",&n); rep(i,1,n){ scanf("%lld%lld",&p[i],&d[i]); a[i+n]=a[i]=p[i]-d[i]; sum[i]=sum[i-1]+a[i]; } rep(i,1+n,n+n) sum[i]=sum[i-1]+a[i]; rep(i,1,2*n-1){ while(!Q.empty() && Q.front().first+n<=i) Q.pop_front(); while(!Q.empty() && Q.back().second>=sum[i]) Q.pop_back(); Q.push_back(make_pair(i,sum[i])); if(i>=n){ if(Q.front().second-sum[i-n]<0) ans[i-n+1]=0; else ans[i-n+1]=1; } } p1[1]=p[1]; d1[1]=d[n]; a[1]=a[1+n]=p1[1]-d1[1]; sum[1]=a[1]; rep(i,2,n){ p1[i]=p[n-i+2]; d1[i]=d[n-i+1]; a[i]=a[i+n]=p1[i]-d1[i]; sum[i]=sum[i-1]+a[i]; } rep(i,n+1,n*2) sum[i]=sum[i-1]+a[i]; rep(i,1,2*n-1){ while(!Q.empty() && Q.front().first+n<=i) Q.pop_front(); while(!Q.empty() && Q.back().second>=sum[i]) Q.pop_back(); Q.push_back(make_pair(i,sum[i])); if(i>=n) if(Q.front().second-sum[i-n]>=0) ans[work(i)]=1; } rep(i,1,n){ if(ans[i]) puts("TAK"); else puts("NIE"); } return 0; }