题解:P9312 [EGOI 2021] Lanterns / 灯笼
UKE_Automation · · 题解
P9312 [EGOI 2021] Lanterns / 灯笼
先考虑朴素的 dp 怎么做:枚举起点
考虑优化,首先要优化的就是将枚举起点这一步搞掉,方法很简单,转置一手即可。然后由于起点未知,
这样做其实没有优化复杂度,不过这时候的瓶颈就在状态上了。考虑怎样优化状态,我们想办法把海拔上下界和区间位置这两个信息压缩起来,这时候不难想到,由于上下界是由区间中某两个点确定的,所以只需要知道这两个点的位置,就可以计算出区间位置和上下界了。所以设
考虑转移,枚举区间中的灯笼,然后依然判断交集并转移即可,分类讨论一下有:
此时的复杂度是
于是我们就可以对
对于第二种转移优化方式和第一种类似,不再赘述。现在看第三种转移,一个经典的思路是我们分步转移,也就是
如此我们的 dp 就优化到了
#include <bits/stdc++.h>
#define il inline
using namespace std;
const int Maxn = 2e3 + 5;
const int Inf = 2e9 + 5;
const int Mod = 1e9 + 7;
il int Add(int x, int y) {return x + y >= Mod ? x + y - Mod: x + y;} il void pls(int &x, int y) {x = Add(x, y);}
il int Del(int x, int y) {return x - y < 0 ? x - y + Mod : x - y;} il void sub(int &x, int y) {x = Del(x, y);}
il int max(int x, int y) {return x >= y ? x : y;} il void chkmax(int &x, int y) {x = (x >= y ? x : y);}
il int min(int x, int y) {return x <= y ? x : y;} il void chkmin(int &x, int y) {x = (x <= y ? x : y);}
il int qpow(int a, int b) {int res = 1; for(; b; a = 1ll * a * a % Mod, b >>= 1) if(b & 1) res = 1ll * res * a % Mod; return res;}
il int Inv(int a) {return qpow(a, Mod - 2);}
template <typename T>
il void read(T &x) {
x = 0; char ch = getchar(); bool flg = 0;
for(; ch < '0' || ch > '9'; ch = getchar()) flg = (ch == '-');
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
flg ? x = -x : 0;
}
template <typename T>
il void write(T x, bool typ = 1) {
static short Stk[50], Top = 0;
x < 0 ? putchar('-'), x = -x : 0;
do Stk[++Top] = x % 10, x /= 10; while(x);
while(Top) putchar(Stk[Top--] | 48);
typ ? putchar('\n') : putchar(' ');
}
il void IOS() {ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);}
il void File() {freopen("lantern.in", "r", stdin); freopen("lantern.out", "w", stdout);}
bool Beg;
int n, m, h[Maxn];
int p[Maxn], c[Maxn], a[Maxn], b[Maxn];
int id1[Maxn], id2[Maxn];
int l[Maxn][2], r[Maxn][2];
int dp[Maxn][Maxn];
#define pii pair<int, int>
#define mk make_pair
priority_queue <pii, vector<pii>, greater<pii>> q1[Maxn], q2[Maxn];
bool End;
il void Usd() {cerr << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n"; }
int main() {
File();
read(n), read(m);
for(int i = 1; i <= n; i++) read(h[i]);
for(int i = 1; i <= m; i++) read(p[i]), read(c[i]), read(a[i]), read(b[i]), id1[i] = id2[i] = i;
sort(id1 + 1, id1 + m + 1, [](int x, int y){return a[x] < a[y];});
sort(id2 + 1, id2 + m + 1, [](int x, int y){return b[x] > b[y];});
for(int i = 1; i <= m; i++) {
l[i][0] = l[i][1] = r[i][0] = r[i][1] = p[i];
while(l[i][0] > 1 && h[l[i][0] - 1] >= a[i]) l[i][0]--;
while(r[i][0] < n && h[r[i][0] + 1] >= a[i]) r[i][0]++;
while(l[i][1] > 1 && h[l[i][1] - 1] <= b[i]) l[i][1]--;
while(r[i][1] < n && h[r[i][1] + 1] <= b[i]) r[i][1]++;
}
for(int i = 1; i <= m; i++) { //最小值
for(int j = 1; j <= m; j++) { //最大值
int x = id1[i], y = id2[j];
dp[x][y] = Inf;
if(p[x] < l[y][1] || p[x] > r[y][1] || p[y] < l[x][0] || p[y] > r[x][0]) continue;
if(a[y] < a[x] && b[x] > b[y]) continue;
int pl = max(l[x][0], l[y][1]), pr = min(r[x][0], r[y][1]);
if(pl == 1 && pr == n) dp[x][y] = 0;
else if(a[y] < a[x]) dp[x][y] = dp[y][y];
else if(b[x] > b[y]) dp[x][y] = dp[x][x];
else {
while(!q1[x].empty()) {
int t = q1[x].top().second;
if(p[t] < pl || p[t] > pr || b[t] < a[x] || a[t] > b[y]) q1[x].pop();
else break;
}
if(q1[x].size()) chkmin(dp[x][y], q1[x].top().first);
while(!q2[y].empty()) {
int t = q2[y].top().second;
if(p[t] < pl || p[t] > pr || b[t] < a[x] || a[t] > b[y]) q2[y].pop();
else break;
}
if(q2[y].size()) chkmin(dp[x][y], q2[y].top().first);
}
if(dp[x][y] != Inf) {
q1[x].push(mk(dp[x][y] + c[y], y));
q2[y].push(mk(dp[x][y] + c[x], x));
}
}
}
for(int i = 1; i <= m; i++) {
if(h[p[i]] < a[i] || h[p[i]] > b[i] || dp[i][i] == Inf) write(-1);
else write(dp[i][i] + c[i]);
}
Usd();
return 0;
}