题意:
Pinball的游戏界面由m+2行、n列组成。第一行在顶端。一个球会从第一行的某一列出发,开始垂直下落,界面上有一些漏斗,一共有m个漏斗分别放在第2~m+1行,第i个漏斗的作用是把经过第i+1行且列数在Ai~Bi之间的球,将其移到下一行的第Ci列。 使用第i个漏斗需要支付Di的价钱,你需要保留一些漏斗使得球无论从第一行的哪一列开始放,都只可能到达第m+2行的唯一 一列,求花费的最少代价。
(样例的图)
(我们保留2,4,5即可,代价为5+3+12=20)
Input
第一行两个数,m和n。m<=100000,2<=n<=1000000000 接下来m行,第i+1行描述第i个漏斗的属性,Ai,Bi,Ci,Di (1<=Ai<=Ci<=Bi<=n, 1<=Di<=1000000000)。
Output
若不存在一种方案能满足条件则输出-1,否则输出最小花费
Input示例
5 6 3 5 4 8 1 4 3 5 4 6 5 7 5 6 5 3 3 5 4 12
Output示例
20
思路:
好题,一开始感觉是离散化线段树,但是想不出来怎么dp,看了别人的思路才恍然大悟。
这道题的关键在于要看出来最后只需要处理从1列出发的以及从n列出发的球能否到达同一列即可。
因为对于假设a[i]是第i列的球最终能到达的列数,那么如果i < j,则必定a[i] <= a[j],画一画就能看出来,这样的话,如果第1列的球最终能到达第x列,而且第n列的球最终也能到达第x列,那1到n之间的球都能到达x列。所以题目所要求的其实就是看1列和n列是否满足而已。
这里设dp1[i]为第1列到到达第i个漏斗的最小代价,dpn[i]为第n列到达第i个漏斗的最小代价,最终答案就是min(dp1[i] + dpn[i] - a[i].v)。最后减掉的a[i].v是dp1[i]和dpn[i]重复算了两次的,另外,不用担心在这之前会存在1列的路径上和n列的路径上的代价有重复的情况,因为如果发生这种情况,说明1列和n列的球在之前已经到达同一个漏斗,这样的结果一定比当前计算的结果更优。
知道这个状态方程,剩下的很显然要有线段树优化,另外要注意每个漏斗的a,b,c都要进行离散化,1和n也必须要进行离散化。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
typedef long long ll;
const int MAXN = 3e5 + 10;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
ll minx[MAXN << 2];
void pushup(int rt) {
minx[rt] = min(minx[rt << 1], minx[rt << 1 | 1]);
}
void build(int l, int r, int rt) {
minx[rt] = INF;
if (l == r) return;
int m = (l + r) >> 1;
build(lson);
build(rson);
pushup(rt);
}
ll query(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) return minx[rt];
int m = (l + r) >> 1;
ll res = INF;
if (L <= m) res = min(res, query(L, R, lson));
if (R > m) res = min(res, query(L, R, rson));
return res;
}
void update(int pos, ll val, int l, int r, int rt) {
if (l == r) {
minx[rt] = min(minx[rt], val);
return;
}
int m = (l + r) >> 1;
if (pos <= m) update(pos, val, lson);
else update(pos, val, rson);
pushup(rt);
}
struct node {
int l, r, c, v;
} a[MAXN];
int discre(int m, int n) {
vector <int> vec;
vec.push_back(1); vec.push_back(n);
for (int i = 1; i <= m; i++) {
vec.push_back(a[i].l); vec.push_back(a[i].r); vec.push_back(a[i].c);
}
sort(vec.begin(), vec.end());
vec.erase(unique(vec.begin(), vec.end()), vec.end());
for (int i = 1; i <= m; i++) {
a[i].l = lower_bound(vec.begin(), vec.end(), a[i].l) - vec.begin() + 1;
a[i].r = lower_bound(vec.begin(), vec.end(), a[i].r) - vec.begin() + 1;
a[i].c = lower_bound(vec.begin(), vec.end(), a[i].c) - vec.begin() + 1;
}
return lower_bound(vec.begin(), vec.end(), n) - vec.begin() + 1;
}
ll dp1[MAXN], dpn[MAXN];
int main() {
int m, n;
scanf("%d%d", &m, &n);
for (int i = 1; i <= m; i++)
scanf("%d%d%d%d", &a[i].l, &a[i].r, &a[i].c, &a[i].v);
if (n == 1) {
puts("0");
return 0;
}
n = discre(m, n); // 更新离散化之后的n
build(1, n, 1);
for (int i = 1; i <= m; i++) {
dp1[i] = a[i].l <= 1 ? a[i].v : INF;
dp1[i] = min(dp1[i], query(a[i].l, a[i].r, 1, n, 1) + a[i].v);
if (dp1[i] != INF) update(a[i].c, dp1[i], 1, n, 1);
}
build(1, n, 1);
for (int i = 1; i <= m; i++) {
dpn[i] = a[i].r >= n ? a[i].v : INF;
dpn[i] = min(dpn[i], query(a[i].l, a[i].r, 1, n, 1) + a[i].v);
if (dpn[i] != INF) update(a[i].c, dpn[i], 1, n, 1);
}
ll ans = INF;
for (int i = 1; i <= m; i++) {
//printf("%d %d %d\n", a[i].l, a[i].r, a[i].c);
//printf("%lld %lld\n", dp1[i], dpn[i]);
ans = min(ans, dp1[i] + dpn[i] - a[i].v);
}
if (ans == INF) puts("-1");
else printf("%lld\n", ans);
return 0;
}