题目大意:
一条长度为L的路上有n个路灯,每个路灯能照亮的范围互不重叠。
现在你要一边走路一边唱歌,唱一首歌的同时可以走p的路程。
你要么一直唱下去,要么停一会继续唱,一首歌必须唱完才能停下。
歌唱一旦停止,就至少经过t的路程才能继续唱。
为了不伤及无辜,你不能在黑的地方唱歌。
问最多能唱多少首歌。
思路:
很容易想到一个O(n^2)的DP。
用f[i]表示走完第i个路灯能唱的歌数,用g[i]表示走完第i个路灯并唱完f[i]首歌能走到的位置。
状态转移方程:
f[i]=max{f[j]+r-max(l,g[j]+t)/p}
g[i]=max{r-(r-max(l,g[j]+t))%p}
这样会在第14个点TLE,考虑把转移优化成O(1)的:
如果i的答案不够优,我们就用i-1的答案代替i。
很显然,f和g都是单调不下降的。
一个状态j可以被转移到i当且仅当x<=j<=y。
其中x表示满足g[x]+t<=l的最后一个状态;
y表示满足g[x]+t<=r的最后一个状态。
所以我们每次只需要从j转移过来即可。
除了x和y会被转移两次,其它都只会转移一次,所以时间复杂度是O(n)的。
另外需要注意当转移前后的f相等时,g更小的更优,要转移过去,否则会在第14个点WA。
1 #include<cstdio> 2 #include<cctype> 3 #include<algorithm> 4 inline int getint() { 5 register char ch; 6 while(!isdigit(ch=getchar())); 7 register int x=ch^'0'; 8 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 9 return x; 10 } 11 const int N=100001; 12 int f[N],g[N]; 13 int main() { 14 const int L=getint(),n=getint(),p=getint(),t=getint(); 15 g[0]=-t; 16 for(register int i=1,j=0;i<=n;i++) { 17 const int l=getint(),r=getint(); 18 while(j<i&&g[j]+t<=r) { 19 if(f[j]+(r-std::max(l,g[j]+t))/p>f[i]||f[j]+(r-std::max(l,g[j]+t))/p==f[i]&&r-(r-std::max(l,g[j]+t))%p<g[i]) { 20 f[i]=f[j]+(r-std::max(l,g[j]+t))/p; 21 g[i]=r-(r-std::max(l,g[j]+t))%p; 22 } 23 j++; 24 } 25 j--; 26 if(f[i-1]>f[i]||f[i-1]==f[i]&&g[i-1]<g[i]) { 27 f[i]=f[i-1]; 28 g[i]=g[i-1]; 29 } 30 } 31 printf("%d ",f[n]); 32 return 0; 33 }