您的位置:首页 > 其它

2015-2016 ACM-ICPC, NEERC, Northern Subregional Contest Problem J 【二分+DP+单调队列】

2016-09-11 15:04 405 查看
题目链接

题意

有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]+(i−x)

其中 dp[x]=min{dp[i−r]→dp[i−1]}

同时考虑到,我们始终是要从起始站坐到终点站的,又因为往回坐始终不是最优的选择,因此地铁运行所带来的时间花费始终是n-1,因此我们在状态转移的时候不考虑它,而在最后时间加上n-1即可

dp[i]=min{dp[i−r]→dp[i−1]}+d[i]

最终时间为 dp[n−1]+n−1

对于min{dp[i−r]→dp[i−1]},可以用单调队列优化或者直接用线段树,但单调队列在时间上会更优一些。

那么再考虑买哪一种票,首先能够走得更远但价格便宜的票肯定更优,于是先处理一下,去掉价格贵但走不远的票。这样处理后,所有票都会是按照价格递增,距离递增的顺序出现,二分枚举答案即可。

时间复杂度: 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐