1. 程式人生 > >[POI2014]DOO-Around the world

[POI2014]DOO-Around the world

通過幾年的努力,Byteasar最終拿到了飛行員駕駛證。為了慶祝這一事實,他打算買一架飛機並且繞Byteotia星球赤道飛行一圈。但不幸的是赤道非常長所以需要中途加幾次油。現在已知赤道上面所有飛機場,所有飛機從飛機場起飛降落也可以加油。因為買飛機是個十分重大的決定,Byteasar決定尋求你的幫助。他將會讓你模擬不同的飛行路線。自然這些飛機一次能走的航程是不同的。對於每次模擬,他想要知道最少需要降落多少次(包括最後一次)。需要注意的是起點可以任意選取。

 

詢問<=100,飛機場<=1e6

O(1e8)可過。

 

斷環成鏈,複製一倍。

假設出發點都在[n+1,n+n],往左走。(即逆時針)

fa[i]表示i起點,終點在哪裡(可能不是真正的終點)

dp[i]表示,i到終點fa[i]的最少步數。

那麼,每次找到能走的最遠的j,fa[i]=fa[j],dp[i]=dp[j]+1

然後出現i-fa[i]>=n的情況,直接輸出dp[i]

 

原因:

首先這樣可以遍歷所有的出發點,可以遍歷到最優解。

即使fa[i]開始並不是真正的終點,但是可以在後面列舉到。

假如n+1開始的解是這樣。

我們開始列舉n+1的第一步能跳的。於是n+1的終點是:

但是上面的情況也能列舉到。由於是一個環,所以在後面的部分會列舉到這種情況。

就像這樣。

本質是同一種情況。

 

為什麼可以直接退出?

因為如果當前滿足的話,不存在一個更靠後的位置,使得比dp[pos]還大。

如果存在,一定會出現這種情況:

綠色這裡,藍色決策直接跨過了紅色的一段。因為紅色找的是最遠 的能跳的,所以這樣肯定是不合法的。

 

 

程式碼:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int N=1000000+5;
int n,s;
int fa[2*N],dp[2*N],sum[2
*N]; int main(){ scanf("%d%d",&n,&s);int x; int mx=0; for(ri i=1;i<=n;++i){ scanf("%d",&x); mx=max(mx,x); fa[i]=i; sum[i]=sum[i-1]+x; } for(ri i=n+1;i<=n+n;++i) sum[i]=sum[i-1]+sum[i-n]-sum[i-n-1]; while(s--){ int d;scanf("%d",&d); if(d<mx) printf("NIE\n"); else for(ri i=n+1,j=1;i<=n+n;++i){ while(sum[i]-sum[j]>d) ++j; dp[i]=dp[j]+1; fa[i]=fa[j]; if(i-fa[i]>=n){ printf("%d\n",dp[i]);break; } } }return 0; }