【ybt高效进阶5-6-4】【luogu CF372C】燃放烟火 / Watching Fireworks is Fun(单调队列优化DP)
燃放烟火 / Watching Fireworks is Fun
题目链接:ybt高效进阶5-6-4 / luogu CF372C
题目大意
有一个长度为 n 的路,然后每个 1 个长度有一个点。
然后有 m 次烟火,都有放的时间和地点。
然后如果有一个烟火在 x 放了,你在 y,那这次的分数就是这个烟火本身的分数减去你和烟火的距离。
然后你初始的位置随意,你每个时间可以移动不超过 d 个距离。
然后要你最大化并输出你总共获得的分数和。
思路
(麻了一本通上写的读入是 a , t , b a,t,b a,t,b 的读入的还是 a , b , t a,b,t a,b,t,非常差评)
首先普通的 DP。
设
f
i
,
j
f_{i,j}
fi,j 为
i
i
i 个时刻,你在
j
j
j 的最大分数。
然后发现时刻很多,但烟花很少,考虑离散化,并将同一个时刻的烟花统一处理。
接着两个时刻之间间隔的时间乘上
d
d
d 就是
d
i
s
dis
dis,你这次可以走的长度。(防止爆 int 我就直接和
n
n
n 去
min
\min
min,反正无所谓)
然后
f
i
,
j
=
min
∣
j
−
k
∣
⩽
d
i
s
{
f
i
−
1
,
k
}
+
g
e
t
(
i
,
j
)
f_{i,j}=\min\limits_{|j-k|\leqslant dis}\{f_{i-1,k}\}+get(i,j)
fi,j=∣j−k∣⩽dismin{fi−1,k}+get(i,j)。
(
g
e
t
(
i
,
j
)
get(i,j)
get(i,j) 是第
i
i
i 个时刻你在
j
j
j 会获得的分数,可以直接很快的求出)
然后不难想到就是顺着倒着一个单调队列就可以优化掉,过掉。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
struct node {
int a, t, b;
}a[150001];
int n, m, d, now, tot;
int sta[150001], l, r;
ll f[2][150001], ans;
bool cmp(node x, node y) {
return x.t < y.t;
}
int abss(int x) {
if (x < 0) return -x;
return x;
}
int main() {
scanf("%d %d %d", &n, &m, &d);
for (int i = 1; i <= m; i++) {
scanf("%d %d %d", &a[i].a, &a[i].b, &a[i].t);
}
// sort(a + 1, a + m + 1, cmp);
memset(f, -0x7f, sizeof(f));
for (int i = 1; i <= n; i++) f[0][i] = 0;
a[0].t = 1;
now = 1;
for (int i = 1; now <= m; i++) {
tot++;
int st = now;
now++;
while (now <= m && a[now].t == a[now - 1].t) {
now++;
}
int ed = now - 1;
int dis = min(1ll * n, 1ll * d * (a[st].t - a[st - 1].t));
l = 1; r = 0;//从前面过去
for (int j = 1; j <= n; j++) {
while (l <= r && j - sta[l] > dis) l++;
if (l <= r) f[i & 1][j] = max(f[i & 1][j], f[(i + 1) & 1][sta[l]]);
while (l <= r && f[(i + 1) & 1][sta[r]] <= f[(i + 1) & 1][j]) r--;
sta[++r] = j;
}
l = 1; r = 0;//从后面过来
for (int j = n; j >= 1; j--) {
while (l <= r && sta[l] - j > dis) l++;
if (l <= r) f[i & 1][j] = max(f[i & 1][j], f[(i + 1) & 1][sta[l]]);
while (l <= r && f[(i + 1) & 1][sta[r]] <= f[(i + 1) & 1][j]) r--;
sta[++r] = j;
}
for (int j = 1; j <= n; j++) {//计算每个位置在这个时刻的分数并加上
f[i & 1][j] = max(f[i & 1][j], f[(i + 1) & 1][j]);
ll newadd = 0;
for (int k = st; k <= ed; k++) newadd += 1ll * (a[k].b - abss(a[k].a - j));
f[i & 1][j] += newadd;
}
memset(f[(i + 1) & 1], -0x7f, sizeof(f[(i + 1) & 1]));
}
ans = -INF;
for (int i = 1; i <= n; i++)
ans = max(ans, f[tot & 1][i]);
printf("%lld", ans);
return 0;
}
相关文章
- Hbase Compaction 队列数量较大分析(压缩队列、刷新队列)
- 利用 rpush 和 blpop 实现 Redis 消息队列
- 【华为OD机试真题 python】特异性双端队列 | 最小调整顺序次数【2022 Q4 | 100分】
- 【BZOJ2806】[Ctsc2012]Cheat 广义后缀自动机+二分+单调队列优化DP
- kafka 查看队列信息
- 【万字总结】图解堆算法、链表、栈与队列(多图预警)
- 流媒体学习四------- ortp队列的实现
- 栈和队列面试题讲解
- 基于Python实现数据包队列管理内容的实验【100010465】
- java并发之阻塞队列LinkedBlockingQueue与ArrayBlockingQueue
- 浅析进程是什么(代码、数据、pcb)、本地进程通信的4种机制(信号量、管道、消息队列、共享内存)、ipc/rpc/lpc是什么、electron进程通信(ipcMain、ipcRenderer、remote)、nodejs进程通信(child_process、cluster)
- 使用二叉堆实现优先队列
- 第2章第2节练习题3 使用队列模拟渡口管理
- 消息队列系列(三):.Rabbitmq Trace的使用
- 消息队列技术选型(Kafka + RocketMQ)