P11587 题解
DaiRuiChen007 · · 题解
Problem Link
题目大意
给定
a_1\sim a_n,b_1\sim b_{n-1} ,每个b_i 可以选择x\in[0,b_i] ,给a_i 加上x ,a_{i+1} 加上b_i-x 。数据范围:$n,q\le 10^5$。
思路分析
首先二分答案
用 Hall 定理判定,显然只要考虑左部点集是一个区间的情况,即对每个子区间
那么答案就是所有子区间
稍加转化变成求区间中最小的
看成求
对于一般的情况可以分块,隔
此时一个查询只剩下
时间复杂度
我们尝试优化掉二分,用单调队列维护切点。
这个做法的正确性依赖于决策单调性,即每个点的最优转移点递增。
假设
首先
同理
那么
因此我们直接单调队列维护,虽然不能保证每个
那么查询的是就不用在凸壳上二分,直接单调队列维护切点即可。
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
#define LL __int128
using namespace std;
const int MAXN=1e5+5;
const ll inf=1e18;
int n,m;
ll a[MAXN],b[MAXN];
struct poi { ll x,y; };
bool chk(poi u,poi v,poi q) { //slope(u,q)<slope(v,q)
return (LL)(q.y-u.y)*(q.x-v.x)<(LL)(q.y-v.y)*(q.x-u.x);
}
poi st[MAXN];
int hd,tl;
void ins(poi o) {
while(tl>hd&&chk(st[tl-1],st[tl],o)) --tl;
st[++tl]=o;
}
ll qry(poi o) {
if(hd>tl) return inf;
while(hd<tl&&chk(st[hd+1],st[hd],o)) ++hd;
return (o.y-st[hd].y)/(o.x-st[hd].x);
}
const int K=360;
ll f[MAXN/K+5][MAXN];
ll sol(int l,int r) {
if(l%K==0) return f[l/K][r];
if(r%K==0) return f[r/K][l];
if(l/K==r/K) {
ll s=inf; hd=1,tl=0;
for(int i=l;i<=r;++i) s=min(s,qry({i,a[i]})),ins({i,b[i]});
return s;
}
ll s=min(f[l/K+1][r],f[r/K][l]); hd=1,tl=0;
for(int i=l;i<(l/K+1)*K;++i) s=min(s,qry({i,a[i]})),ins({i,b[i]});
for(int i=r/K*K+1;i<=r;++i) s=min(s,qry({i,a[i]})),ins({i,b[i]});
return s;
}
vector<int> testset(vector<int>A,vector<int>B,vector<int>QL,vector<int>QR) {
n=A.size(),m=QL.size();
for(int i=1;i<=n;++i) {
a[i]=a[i-1]+A[i-1]+(i<n?B[i-1]:0);
b[i]=b[i-1]+A[i-1]+(i>1?B[i-2]:0);
}
for(int o=0,x;o*K<=n;++o) {
ll *dp=f[o]; dp[x=o*K]=inf;
hd=1,tl=0,ins({x,b[x]});
for(int i=x+1;i<=n;++i) dp[i]=min(dp[i-1],qry({i,a[i]})),ins({i,b[i]});
hd=1,tl=0,ins({-x,-a[x]});
for(int i=x-1;i>=0;--i) dp[i]=min(dp[i+1],qry({-i,-b[i]})),ins({-i,-a[i]});
}
vector <int> ans;
for(int i=0;i<m;++i) ans.push_back(sol(QL[i],QR[i]+1));
return ans;
}