题目大意
在一条数轴上,你要从0走到
L
。
其中有
你要选最多的长度为
p
的段,使得每一段都在可行区域内。
并且假如你现在取的段是
start=x+p
或
start≥x+p+t
.
问最多取多少段?
解题思路
设
fi
为走完第
i
个区间的最优值。
设
li,ri
为第
i
个区间的两个端点。那么:
gi=max{li,gk+t}+⌊(ri−max{li,gk+t})p⌋⋅p
假如 i 由
容易看出,因为区间不相交,所以 gi 单掉不减,决策满足单调性,我们用一个单调队列来维护。每次取出队首满 gj+t≤ri 的状态,更新当前状态。如果当前状态优于历史最优解,入队。注意取出的最后一个满足 gj+t≤ri 的状态还可以用来更新下一个状态,它不出队。
参考程序
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define maxn 100005
#define min(a,b) (((a) < (b)) ? a : b)
#define max(a,b) (((a) > (b)) ? a : b)
using namespace std;
int f[maxn],g[maxn];
int l,n,p,t;
int head,tail;
int ans;
int main(){
scanf("%d%d%d%d",&l,&n,&p,&t);
head=1;
tail=1;
f[tail]=0;
g[tail]=-t;
fo(i,1,n) {
int x,y,ans1,ans2;
scanf("%d%d",&x,&y);
ans1=ans2=0;
if (head>1) head--;
while (head<=tail && g[head]+t<=y) {
int nx=max(x,g[head]+t),ny=y;
if (f[head]+(ny-nx)/p>ans1) {
ans1=f[head]+(ny-nx)/p;
ans2=nx+(ny-nx)/p*p;
}
else if (f[head]+(ny-nx)/p==ans1) {
ans2=min(ans2,nx+(ny-nx)/p*p);
}
++head;
}
if (ans1>ans) {
ans=ans1;
tail++;
f[tail]=ans1;
g[tail]=ans2;
}
}
printf("%d",ans);
return 0;
}