CF2229G Roadworks
\(\text{OI}\) 退役后第一篇题解!
思路
考虑在村庄间移动的过程,容易发现一定形如:直接走到某个点,在这个点停留一会,走到另一个更大的点,再停留一会......
也就是停留的点一定是行走路径上最大的那个。

容易想到把路径拆分成从某个点到其左边或右边第一个更大值,随后在这个点停留几天(可能是 \(0\))的几段。
并且到达新村庄的时间一定是允许范围内最小的,因为在新村庄停留一定更优。
设 \(t_i\) 表示到达 \(i\) 的最小时间,\(f_i\) 表示在时间最小的情况下的最大满意度。
\(t_i\) 可以直接算出来,就是从 \(x\) 出发到 \(i\) 的最小时间,现在只需计算 \(f_i\) 即可。
可以类似 dijkstra 地转移,不过也无须如此麻烦,我们可以直接钦定一个转移顺序,例如按 \(h\) 值从小到大转移。
统计答案是简单的,这样就能做到 \(\Theta(n \log n)\),瓶颈在于排序。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mid ((l+r)>>1)
#define lowbit(x) ((x)&(-x))
#define all(x) (x).begin(),(x).end()
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define add(x,y) (x=((x+y>=mod)?(x+y-mod):(x+y)))
int const N=2e5+10;
int n,k,x,top,S[N],t[N],f[N],h[N],d[N],L[N],R[N],id[N],qz[N];
inline void solve(){
cin>>n>>k>>x;
rep(i,1,n) cin>>h[i],id[i]=i;
rep(i,1,n-1) cin>>d[i];
rep(i,1,n) qz[i]=qz[i-1]+h[i];
top=0;
rep(i,1,n) L[i]=R[i]=f[i]=0;
rep(i,1,n){
while (top && h[i]>h[S[top]]) R[S[top]]=i,--top;
S[++top]=i;
}
top=0;
per(i,n,1){
while (top && h[i]>h[S[top]]) L[S[top]]=i,--top;
S[++top]=i;
}
sort(id+1,id+n+1,[](int x,int y){return h[x]<h[y];});
t[x]=0;
rep(i,x+1,n) t[i]=max(t[i-1]+1,d[i-1]);
per(i,x-1,1) t[i]=max(t[i+1]+1,d[i]);
t[x]=1;
int ans=h[x]*k;
rep(i,1,n){
int nw=id[i];
ans=max(ans,f[nw]+(k-t[nw]+1)*h[nw]);
if (L[nw]){
int to=L[nw];
if (nw-to<=t[to]-t[nw]) f[to]=max(f[to],f[nw]+(t[to]-t[nw]-(nw-to)+1)*h[nw]+qz[nw-1]-qz[to]);
}
if (R[nw]){
int to=R[nw];
if (to-nw<=t[to]-t[nw]) f[to]=max(f[to],f[nw]+(t[to]-t[nw]-(to-nw)+1)*h[nw]+qz[to-1]-qz[nw]);
}
}
cout<<ans<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
int t=1;
cin>>t;
while (t--) solve();
// cerr<<"Time:"<<(double)clock()/CLOCKS_PER_SEC<<"s\n";
return 0;
}

浙公网安备 33010602011771号