P9312 题解
DaiRuiChen007 · · 题解
P9312 题解
Problem Link
题目大意
平面直角坐标系上有
n 个点,坐标为(1,h_1)\sim (n,h_n) ,保证h 是一个1\sim n 的排列,对于横坐标相邻的点有线段连接,你的目标是沿着连接相邻两个点的线段遍历这n 个点。有
m 个区间,第i 个区间[l_i,r_i] 有价格w_i ,需要在p_i 上购买,你能从h_i 移动到h_{i+1} 当且仅当[h_i,h_{i+1}] 中的每个x 都至少被一个你购买的区间覆盖。对于每个区间
[l_i,r_i] 求出:你购买[l_i,r_i] 后从p_i 开始遍历所有点的最小花费。数据范围:
n,m\le 2000 。
思路分析
考虑 dp 状态:
注意到一个观察:对于一个横坐标的区间
考虑进一步简化状态,注意到当我们确定覆盖区间
而注意到
考虑转移的形式:
即新购买的线段分别更新了左端点 / 右端点 / 同时更新
考虑优化第一个转移,注意到一个结论:若
因此我们对于一个确定的
对于第二种转移同理,我们对每个确定的
考虑第三种转移:
按
时间复杂度:
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2001,INF=2e9;
int h[MAXN],L[MAXN],R[MAXN],P[MAXN],W[MAXN];
int dp[MAXN][MAXN]; //lanterns covered L[l]~R[r]
int lb[MAXN][2],rb[MAXN][2]; //section >=L[i], section <=R[i]
priority_queue <array<int,2>,vector<array<int,2>>,greater<array<int,2>>> Ql[MAXN],Qr[MAXN];
//best strategy when fix lp/rp
signed main() {
freopen("code.in","r",stdin);
freopen("code.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&h[i]);
for(int i=1;i<=m;++i) scanf("%d%d%d%d",&P[i],&W[i],&L[i],&R[i]);
vector <int> ordL(m),ordR(m);
iota(ordL.begin(),ordL.end(),1);
sort(ordL.begin(),ordL.end(),[&](int x,int y){ return L[x]<L[y]; });
iota(ordR.begin(),ordR.end(),1);
sort(ordR.begin(),ordR.end(),[&](int x,int y){ return R[x]>R[y]; });
for(int i=1;i<=m;++i) {
for(int &p=lb[i][0]=P[i]+1;p>1&&h[p-1]>=L[i];--p);
for(int &p=lb[i][1]=P[i]-1;p<n&&h[p+1]>=L[i];++p);
for(int &p=rb[i][0]=P[i]+1;p>1&&h[p-1]<=R[i];--p);
for(int &p=rb[i][1]=P[i]-1;p<n&&h[p+1]<=R[i];++p);
}
for(int i:ordL) for(int j:ordR) {
dp[i][j]=INF;
int l=max(lb[i][0],rb[j][0]),r=min(lb[i][1],rb[j][1]);
if(P[i]<l||P[i]>r||P[j]<l||P[j]>r||(R[j]<R[i]&&L[i]>L[j])) continue;
if(l==1&&r==n) dp[i][j]=0;
else if(R[j]<R[i]) dp[i][j]=dp[i][i];
else if(L[i]>L[j]) dp[i][j]=dp[j][j];
else {
auto gval=[&](auto &Q,int il,int ir,int hl,int hr) {
while(!Q.empty()) {
int idx=Q.top()[1];
if(P[idx]<il||P[idx]>ir||R[idx]<hl||L[idx]>hr) Q.pop();
else return Q.top()[0];
}
return INF;
};
dp[i][j]=min(dp[i][j],gval(Ql[i],l,r,L[i],R[j]));
dp[i][j]=min(dp[i][j],gval(Qr[j],l,r,L[i],R[j]));
}
if(dp[i][j]<INF) Ql[i].push({dp[i][j]+W[j],j}),Qr[j].push({dp[i][j]+W[i],i});
}
for(int i=1;i<=m;++i) {
if(dp[i][i]<INF) printf("%d\n",dp[i][i]+W[i]);
else puts("-1");
}
return 0;
}