题解——CF372C Watching Fireworks is Fun

传送门

一道有趣的 DP

题目大意

城镇中有$n$个位置,有$m$个烟花要放。第$i$个烟花放出的时间记为$t_{i}$,放出的位置记为$a_{i}$。如果烟花放出的时候,你处在位置$x$,那么你将收获$b_{i}- \left | a_{i}-x \right |$点快乐值。

初始你可在任意位置,你每个单位时间可以移动不大于$d$个单位距离。现在你需要最大化你能获得的快乐值。

思路:DP+单调队列+滚动数组

有一个显然的动态转移方程:

$f_{i,j}=max \left (f_{i,j},f_{i-1,k}+b_{i}- \left |a_{i}-j \right | \right )$

$f_{i,j}$表示在位置$j$放第$i$个烟花获得的最大值。

则$ans=max_{i=1}^{n}f_{m, i}$。

观察方程,可以看出$b_{i}$可以提出来,而$- \left |a_{i}-j \right |$的值在计算的过程中也是确定的,也可提出来。

最后的式子长这个样子

$f_{i,j}=min \left (f_{i,j},f_{i-1,k} \right )$

$ans=sum_{i=1}^{m}b_{i}-min_{i=1}^{n}f_{m, i}$

感觉可以,一看复杂度$O \left ( n^{2}m \right )$,T到飞起。

从上式中,可以看出$k$的范围是

$j- \left ( t_{i}-t_{i-1} \right ) *d \leq k \leq j+ \left ( t_{i}-t_{i-1} \right ) *d$

如何维护$k$的值?很明显,用单调队列维护,使得其能在均摊$0 \left (1 \right )$的时间复杂度内计算出$min \left (f_{i,j},f_{i-1,k} \right )$, 没学的赶快学

于是复杂度从$O \left ( n^{2}m \right )$降到了$O \left ( nm \right )$。

一看范围,150000。。。直接爆炸。

仔细观察,发现可以用滚动数组优化空间,能过。

代码

#include <iostream>

#define RI register int
typedef long long ll;
const int N = 150001;

using namespace std;

template <class T>
inline void read(T &x) {
    T f = 1; x = 0; char c = getchar();
    while(c > '9' || c < '0') {
        if(c == '-')
            f = -f;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
    x *= f;
}

int n, m, d, t[2];
ll f[2][N], ans = 1e18 + 7, sum;
int l, r, q[N];
int p;

inline int abs(int x) {
    return x > 0 ? x : -x;
}

inline ll min(ll x, ll y) {
    return x > y ? y : x;
}

int main() {
    read(n), read(m), read(d);
    for(RI i = 1; i <= m; i++) {
        int a, b;
        read(a), read(b), read(t[p ^ 1]);
        sum += b;
        ll len = ll(t[p ^ 1] - t[p]) * d;
        l = 1, r = 0;
        for(RI j = 1; j <= n; j++) {
            while(l <= r && q[l] < j - len)
                l++;
            while(l <= r && f[p][q[r]] > f[p][j])
                r--;
            q[++r] = j;
            f[p ^ 1][j] = f[p][q[l]] + abs(a - j);
        }
        l = 1, r = 0;
        for(RI j = n; j >= 1; j--) {
            while(l <= r && q[l] > j + len)
                l++;
            while(l <= r && f[p][q[r]] > f[p][j])
                r--;
            q[++r] = j;
            f[p ^ 1][j] = min(f[p][q[l]] + abs(a - j), f[p ^ 1][j]);
        }
        p ^= 1;
    }
    for(RI i = 1; i <= n; i++)
        ans = min(ans, f[p][i]);
    printf("%lld\n", sum - ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ASTiKi/p/12285911.html