2015-2016 ACM-ICPC, NEERC, Northern Subregional Contest Problem J 【二分+DP+單調隊列】

題目鏈接

題意

有n個地鐵站,全部成線性排列,有n-1種地鐵票,第i種地鐵票的價格爲p_i,並且能坐i站(也就是在第k個站能夠到達[k-i,k+i]中的站)。現在想從起點站坐到終點站,地鐵在相鄰兩個站之間運行花費1s(這裏原文是“get from a stop to the next one in just one minute.”有歧義,坑了好久),給出在每個站出來又進去花費的時間,並且從一個站出來又進去後同一張票又可以重新使用,現給定最大的時間花費,求能夠到達終點站的票中最便宜的價格。

分析

先考慮這樣一個問題,假設固定一次最多能坐到的距離,求到終點的最短時間。很容易想到這樣的轉移方程:
(令dp[i]爲到第i個站花費的最少時間,d[i]是在該站上下所花時間,r是當前票能夠坐的距離)
dp[i]=dp[x]+d[i]+(ix)
其中 dp[x]=min{dp[ir]dp[i1]}

同時考慮到,我們始終是要從起始站坐到終點站的,又因爲往回坐始終不是最優的選擇,因此地鐵運行所帶來的時間花費始終是n-1,因此我們在狀態轉移的時候不考慮它,而在最後時間加上n-1即可

dp[i]=min{dp[ir]dp[i1]}+d[i]
最終時間爲 dp[n1]+n1

對於min{dp[ir]dp[i1]} ,可以用單調隊列優化或者直接用線段樹,但單調隊列在時間上會更優一些。

那麼再考慮買哪一種票,首先能夠走得更遠但價格便宜的票肯定更優,於是先處理一下,去掉價格貴但走不遠的票。這樣處理後,所有票都會是按照價格遞增,距離遞增的順序出現,二分枚舉答案即可。

時間複雜度: O(nlog(n)) (單調隊列) O(nlog2(n)) (線段樹)

AC代碼

//2015-2016 ACM-ICPC, NEERC, Northern Subregional Contest Problem J. Journey to the “The World’s Start”
//AC 2016-9-11 15:02:46
//DP, Binary Search, Monotonic Queue
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <set>
#include <string>
#include <map>
#include <queue>
#include <deque>
#include <list>
#include <sstream>
#include <stack>
using namespace std;

#define cls(x) memset(x,0,sizeof x)
#define inf(x) memset(x,0x3f,sizeof x)
#define neg(x) memset(x,-1,sizeof x)
#define ninf(x) memset(x,0xc0,sizeof x)
#define st0(x) memset(x,false,sizeof x)
#define st1(x) memset(x,true,sizeof x)
#define INF 0x3f3f3f3f
#define lowbit(x) x&(-x)
#define input(x) scanf("%d",&(x))
#define inputt(x,y) scanf("%d %d",&(x),&(y))
#define bug cout<<"here"<<endl;
//#pragma comment(linker, "/STACK:1024000000,1024000000")//stack overflow
#define debug

const int maxn=1000100;
struct autoque
{
    long long que[maxn];
    int index[maxn];
    int l;
    int r;
    int k;
    int cur;
    autoque():l(0),r(0),k(0),cur(0){}
    autoque(int kk):l(0),r(0),k(kk),cur(0)
    {
        memset(que,0,sizeof que);
        return;
    }
    void reset(int kk)
    {
        l=0;r=0;k=kk;cur=0;
        memset(que,0,sizeof que);
    }
    long long front()
    {
        while(index[l]<cur-k)
            ++l;
        return que[l];
    }
    void insert(long long x)
    {
        if(l==r)
        {
            que[r]=x;
            index[r++]=cur++;
            return;
        }
        int ll=l,rr=r;
        int mid;
        while(rr>=ll)
        {
            mid=(ll+rr)/2;
            if(rr==ll)
                break;
            if(x==que[mid])
                break;
            if(x<que[mid])
                ll=mid+1;
            if(x>que[mid])
                rr=mid;
        }
        que[mid]=x;
        index[mid]=cur++;
        r=mid+1;
        return;
    }
}que;

int n;
long long t;
vector<pair<int,int> > p;
int d[50500];
long long dp[50500];

bool valid(int x)
{
    if(x==-1) return 0;
    dp[0]=0;
    que.reset(p[x].first);
    for(int i=1;i<n;++i)
    {
        que.insert(-dp[i-1]);
        dp[i]=-que.front()+d[i];
    }
    return dp[n-1]<=t;
}

int main()
{
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    #ifdef debug
        freopen("journey.in","r",stdin);
        freopen("journey.out","w",stdout);
    #endif
    //IO
    cin>>n>>t;
    t-=(n-1);
    pair<int,int> pri;
    for(int i=0;i<n-1;++i)
    {
        input(pri.second);
        pri.first=i+1;
        while(p.size()&&p.back().second>=pri.second)
            p.pop_back();
        p.push_back(pri);
    }
    for(int i=1;i<n-1;++i)
        input(d[i]);
    d[n-1]=0;
    int ans=INF;
    int l=-1,r=p.size()-1,mid=(l+r)>>1;
    while(l<r-1)
    {
        mid=(l+r)>>1;
        if(valid(mid))
            r=mid;
        else
            l=mid;
    }
    cout<<p[r].second<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章