[CodeForces-721E]Road to Home

题目大意:
  一条长度为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\n",f[n]);
32     return 0;
33 }

 

posted @ 2017-10-10 19:21  skylee03  阅读(159)  评论(0编辑  收藏  举报