codeforces 721E. Road to Home(DP+单调队列)

题目链接http://codeforces.com/contest/721/problem/E
题目大意:在一条坐标范围为[0,L]的路上有n盏路灯,每盏路灯有一个照亮范围[li,ri]且没有重叠部分也不相接。某人想在被路灯照到的地方唱歌,他唱一首歌将走过p的距离,一首歌一定要唱完整。在唱完一首歌后,他可以选择接着唱或者停止,如果停止那么下一次唱歌的位置必须与停止的位置相隔至少t距离。问他最多能唱多少首歌。
数据范围:1 ≤ L ≤ 10^9, 0 ≤ n ≤ 100 000, 1 ≤ p ≤ 10^9, 1 ≤ t ≤ 10^9

题解:简单dp然而愚蠢的我并没有想到。显然我们可以知道,如果他在某一段路上唱歌,那么必然是唱到不能唱为止,即唱到一个位置x满足x+p>r。于是设f[i]表示到第 i 段为止最多能唱多少首歌,g[i]表示唱完f[i]首歌的最左的端点。得到转移方程f[i]=f[j]+(r-max(l,g[j]+t))/p,g[i]=r-(r-max(l,g[j]+t))%p。
我们可以知道 f 和g是非递减的。因为如果i-1的答案比 i 优那么我们直接用i-1替换i。所以可以更新 i 的 j 应该满足xi<=j<=yi,其中xi是最靠后的满足g[xi]+t<=li的点,yi是最靠后的满足g[yi]+t<=ri的点。由于r[i]< l[i+1],所以g[yi]+t< l[i+1],所以[x[i],y[i]]和[x[i+1],y[i+1]]最多只有一个交点,那就是yi。这样更新就是线性的。
时间复杂度O(n)

代码如下:

#include <algorithm>
#include <cstdio>
int f[100010],g[100010],L,n,p,t;
int main(){
    scanf("%d%d%d%d\n",&L,&n,&p,&t);
    g[0]=-t;
    for (int i=1,j=1;i<=n;i++){
        int l,r;
        scanf("%d%d\n",&l,&r);
        g[i]=g[i-1];f[i]=f[i-1];
        for (j--;j<i && g[j]+t<=r;j++){
            int x=std::max(g[j]+t,l),y=f[j]+(r-x)/p,z=r-(r-x)%p;
            if (y>f[i] || (y==f[i] && z<g[i])) f[i]=y,g[i]=z;
        }
    }
    printf("%d\n",f[n]);
}

--------------------- 本文来自 aufeas 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/aufeas/article/details/53039299?utm_source=copy

猜你喜欢

转载自blog.csdn.net/c_czl/article/details/82955571