题目大意:有一条长为l的公路(可看为数轴),n盏路灯,每盏路灯有照射区间且互不重叠,有个人要走过这条公路,他只敢在路灯照射的地方唱歌,固定走p唱完一首歌,歌曲必须连续唱否则就要至少走t才能继续唱。问你最多能唱几首歌?
解题思路:一道dp的题目。
首先有一个结论:对于一段区间,你能唱几首歌就唱几首歌。
因为如果你少唱一首歌,在下个区间最多也就多唱一首歌,与你在前面唱这首歌是一样的。
所以我们设f[i]表示前i个区间最多唱的歌的数量,g[i]表示在f[i]的前提下,最早的停止唱歌的位置。
很显然有$f[i]=max \{ f[j]+\lfloor\frac{(r[i]-max(g[j]+t,l[i]))}{p}\rfloor \}$,$g[i]=min\{ r[i]-(r[i]-max(g[j]+t,l[i]))\mod p \}$ ($j<i$)。
这样的话时间复杂度就是$O(n^2)$,难以接受。
注意到f和g都是满足单调性的(f很显然,g的话,总不可能唱第2首歌比唱第1首歌还前面),所以容易想到单调队列。
这样时间复杂度就优化到$O(n)$。
具体单调队列的操作详见代码。
C++ Code:
#include<cstdio> #include<cstring> #include<cctype> struct Struct{ int f,g; }q[100005]; int l,n,p,t,head,tail; inline int max(int a,int b){return a>b?a:b;} inline int readint(){ char c=getchar(); for(;!isdigit(c);c=getchar()); int d=0; for(;isdigit(c);c=getchar()) d=(d<<3)+(d<<1)+(c^‘0‘); return d; } int main(){ l=readint(),n=readint(),p=readint(),t=readint(); q[0]=(Struct){0,-0x3f3f3f3f}; head=0,tail=0; int ans=0; while(n--){ int x=readint(),y=readint(); int nans=0,ng=0; head?--head:1; while(head<=tail){ if(q[head].g+p+t>y)break; int ff=q[head].f+(y-max(q[head].g+t,x))/p, gg=y-(y-max(q[head].g+t,x))%p; if(ff>nans||ff==nans&&gg<ng)nans=ff,ng=gg; ++head; } if(ans<nans){ ans=nans; q[++tail]=(Struct){nans,ng}; } } return!printf("%d\n",ans); }