题意:长度为nnn的序列,qqq次询问,每次给定一个区间,钦定区间中的一个位置xxx,使得区间所有点 与xxx之间的最大值(含端点) 之和 最小,输出最小值。
n,q≤7.5×105n,q\leq7.5\times10^5n,q≤7.5×105
神仙题,不愧是IOI
首先有一个O(n2)O(n^2)O(n2)的 dp
f(l,r)=min{f(l,k−1)+(r−k+1)hk,(k−l+1)hk+f(k+1,r)}f(l,r)=\min\{f(l,k-1)+(r-k+1)h_k,(k-l+1)h_k+f(k+1,r)\}f(l,r)=min{f(l,k−1)+(r−k+1)hk,(k−l+1)hk+f(k+1,r)}
其中kkk是l,rl,rl,r中的最大值的位置(如有多个随便选一个),即讨论钦定的点在最大值的左侧或右侧,然后另一侧的点贡献都是最大值
我觉得我考场上能想到这步就不错了
这个 dp 转移已经O(1)O(1)O(1)了,也不好压成一维,所以要么用可持久化之类的东西强行压状态,要么就不记录无用的状态
直接想的话两条路都不好走,但注意到这个过程实际上是最值分治,自然地想到笛卡尔树
哪里自然了啊kora
具体地讲,建出序列的笛卡尔树,然后在树上做上面dp,每个点只记录它代表的区间的dp值,这样就可以O(n)O(n)O(n)处理出来了。
然而就算处理出来了你仍然无法快速计算答案,因为询问区间可能会被拆成很多小段,你需要像平衡树一样沿着树递归下去。而笛卡尔树高是O(n)O(n)O(n)的,仍然可以被卡成狗。
不过思路感觉很对,考虑怎么优化
观察一下这个dp方程式
f(l,r)=min{f(l,k−1)+(r−k+1)hk,f(k+1,r)+(k−l+1)hk}f(l,r)=\min\{f(l,k-1)+(r-k+1)h_k,f(k+1,r)+(k-l+1)h_k\}f(l,r)=min{f(l,k−1)+(r−k+1)hk,f(k+1,r)+(k−l+1)hk}
套到树上:当前子树根结点是kkk,为了计算f(l,k−1)f(l,k-1)f(l,k−1)和f(k+1,r)f(k+1,r)f(k+1,r),我们需要继续往左右子树递归计算,我们这样子是不行的
但这个f(l,k−1)f(l,k-1)f(l,k−1)和f(k+1,r)f(k+1,r)f(k+1,r)比较特殊:它们都有一个端点是固定的!
为了叙述方便,下面只讨论f(k+1,r)f(k+1,r)f(k+1,r),左边的f(l,k−1)f(l,k-1)f(l,k−1)是同理的
我们要是知道右子树的区间的所有前缀的dp信息就好了
看上去很扯,但实际上是可行的!
假设我们分别知道kkk的左右子树的前缀信息,也就是知道f(l,l...k−1)f(l,l...k-1)f(l,l...k−1)和f(k+1,k+1...r)f(k+1,k+1...r)f(k+1,k+1...r),现在考虑怎么合并f(l,l...r)f(l,l...r)f(l,l...r)
显然左边是不用管的
对于右边,我们再把这个方程式搬出来。为了看着顺眼,我把rrr换成了iii
f(l,i)=min{f(l,k−1)+(i−k+1)hk,f(k+1,i)+(k−l+1)hk}f(l,i)=\min\{f(l,k-1)+(i-k+1)h_k,f(k+1,i)+(k-l+1)h_k\}f(l,i)=min{f(l,k−1)+(i−k+1)hk,f(k+1,i)+(k−l+1)hk}
注意这个iii是在[k+1,r][k+1,r][k+1,r]内的,冷静分析一下,发现这个东西就是在原来的基础上整体加一个数,然后和一个一次函数取min\minmin,因为是个区间,所以可以用线段树维护!
而这个方程是可以找到一个分界点,使得分界点左边取左边的值,右边取右边的值。原因是iii每增加111,左边的值固定增加hkh_khk,而右边增加f(k+1,i+1)−f(k+1,i)f(k+1,i+1)-f(k+1,i)f(k+1,i+1)−f(k+1,i),区间往右扩张一位增加的代价总不可能大于区间最大值吧……所以左边迟早会超过右边,而且不会被反超。线段树上二分即可。
什么?线段树开不下?
但仔细想想会发现不同的位置是不会冲突的,即每个点xxx维护它当前处理的左端点到xxx的dp值,所以开一个线段树就可以了。
线段树合并应该也可以
f(l,k−1)f(l,k-1)f(l,k−1)的话再开一棵线段树,左右倒过来就可以了
然后把询问离线,每组询问挂在区间最大值的点上面,建笛卡尔树的时候顺便处理一下就可以了。
复杂度O(nlogn)O(n\log n)O(nlogn)
#include <iostream>
#include <cstring>
#include <cctype>
#include <cstdio>
#include <vector>
#define MAXN 750005
using namespace std;
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
typedef long long ll;
int n;
struct SegmentTree
{#define lc p<<1#define rc p<<1|1struct node{int l,r,tag;ll k,b,lv,rv;}t[MAXN<<2];void build(int p,int l,int r){t[p].l=l,t[p].r=r;if (l==r) return;int mid=(l+r)>>1;build(lc,l,mid),build(rc,mid+1,r);}inline void pushcov(int p,ll k,ll b){t[p].k=k,t[p].b=b;t[p].lv=k*t[p].l+b,t[p].rv=k*t[p].r+b;t[p].tag=1;}inline void pushadd(int p,ll k,ll b){t[p].k+=k,t[p].b+=b;t[p].lv+=k*t[p].l+b,t[p].rv+=k*t[p].r+b;if (!t[p].tag) t[p].tag=2; }inline void pushdown(int p){if (t[p].tag){if (t[p].tag==1) pushcov(lc,t[p].k,t[p].b),pushcov(rc,t[p].k,t[p].b);if (t[p].tag==2) pushadd(lc,t[p].k,t[p].b),pushadd(rc,t[p].k,t[p].b);t[p].tag=t[p].k=t[p].b=0;}}inline void update(int p){t[p].lv=t[lc].lv,t[p].rv=t[rc].rv;} ll query(int p,int k){if (t[p].l==t[p].r) return t[p].lv;pushdown(p);if (k<=t[lc].r) return query(lc,k);return query(rc,k);}void search(int p,int l,int r,ll k1,ll b1,ll b2){if (l<=t[p].l&&t[p].r<=r){if (k1*t[p].l+b1<=t[p].lv+b2&&k1*t[p].r+b1<=t[p].rv+b2) return pushcov(p,k1,b1);if (t[p].lv+b2<=k1*t[p].l+b1&&t[p].rv+b2<=k1*t[p].r+b1) return pushadd(p,0,b2);}if (r<t[p].l||t[p].r<l) return;pushdown(p);search(lc,l,r,k1,b1,b2),search(rc,l,r,k1,b1,b2);update(p);}
}lt,rt;
int h[MAXN];
inline int Max(const int& x,const int& y){return h[x]>h[y]? x:y;}
int st[20][MAXN],LOG[MAXN];
inline int rmq(int l,int r)
{int t=LOG[r-l+1];return Max(st[t][l],st[t][r-(1<<t)+1]);
}
int ql[MAXN],qr[MAXN];
ll ans[MAXN];
vector<int> lis[MAXN];
void solve(int l,int r)
{if (l>r) return;int k=rmq(l,r);solve(l,k-1),solve(k+1,r);for (int i=0;i<(int)lis[k].size();i++){int L=ql[lis[k][i]],R=qr[lis[k][i]];ans[lis[k][i]]=h[k]*(R-L+1ll);if (L<k) ans[lis[k][i]]=min(ans[lis[k][i]],rt.query(1,L)+(R-k+1ll)*h[k]);if (k<R) ans[lis[k][i]]=min(ans[lis[k][i]],(k-L+1ll)*h[k]+lt.query(1,R));}ll tl=rt.query(1,l),tr=lt.query(1,r);lt.search(1,k,r,h[k],tl+h[k]*(1ll-k),(k-l+1ll)*h[k]);rt.search(1,l,k,-h[k],tr+h[k]*(k+1ll),(r-k+1ll)*h[k]);
}
int main()
{n=read();int q=read();for (int i=1;i<=n;i++) h[i]=read();lt.build(1,1,n);rt.build(1,1,n);for (int i=1;i<=n;i++) st[0][i]=i;for (int j=1;j<20;j++)for (int i=1;i+(1<<(j-1))<=n;i++)st[j][i]=Max(st[j-1][i],st[j-1][i+(1<<(j-1))]);LOG[0]=-1;for (int i=1;i<=n;i++) LOG[i]=LOG[i>>1]+1;for (int i=1;i<=q;i++) ql[i]=read()+1,qr[i]=read()+1,lis[rmq(ql[i],qr[i])].push_back(i);solve(1,n);for (int i=1;i<=q;i++) printf("%lld\n",ans[i]);return 0;
}