题意
定义数列 \(h[1..n]\) 的 Lipschitz
常数为:
\[\operatorname{L}(h)=\begin{cases} 0&n=1\\ \max\limits_{1\le j< i\le n} {\left\lceil\dfrac{|h[j]-h[i]|}{j-i}\right\rceil}&n\ge 2 \end{cases}\]
现给定一个长度为 \(n\) 的数列 \(h[1..n]\) 以及 \(q\) 个询问。
每个询问给出一个二元组 \((l,r)\) ,要求对于每组询问求出所有子序列的 Lipschitz
常数之和,即求 :
\[\sum\limits_{l\le i\le j\le r} \operatorname{L}(h[i..j])\]
题解
请务必耐心看完切理解题面。
显然不是暴力可以随随便便搞出来的。
当然你也可以使用 ST 表或者线段树,不过这里介绍的是一种最简单的方法,效率也丝毫不亚于上面二者的方法——单调栈。
首先我们有一个命题,这是我们解题的关键。
- 命题:数列 \(a[x..y]\) 的
Lipschitz
常数必定是由数列中 相邻的两项 计算而来。即 \(\operatorname{L}(a[x..y])=\max\limits_{x\le i
接下来给出证明(简单的解释而已,可能不太漂亮):
不妨将 \((i,a[i])\) 视作平面上的一点,这样我们得到 \(\operatorname{size}(a)\) 个点,从而将原命题转化为了这样一个新命题:在上述所有的点中任选两点构成的直线斜率的绝对值之中,最大的必定是由横坐标之差为 \(1\) 的两点(即相邻的)构成。
这个命题相比之前一个要好证很多。我们取这 \(\operatorname{size}(a)\) 个点中任意三个,令其为 \(A,B,C(x_A
- \(k_{AB}
的情况:
图中可见, \(k_{BC}>k_{AC}\)。
- \(k_{AB}>k_{BC}\)
(从左至右分别为点 \(A,B,C\) )
图中可见, \(k_{AB}>k_{AC}\)。
再将其他情况手玩一下,可以发现,最大的斜率一定为相邻的两点所构成的直线的斜率。
于是我们可以很自然地转化 \(\operatorname{L}(h[l..r])\) 为:\(\max\limits_{l\le i\le r} \left\lceil\dfrac{|h_{i+1}-h_i|}{2}\right\rceil\)
设 \(b_{i}=|h_{i+1}-h_{i}|\)
于是原问题就成了求:
\[\sum\limits_{l\le i
行了,经过以上的 瞎扯 ,问题已经原型毕露了。
可以看出这实质上就是一个 一段区间的所有子区间最大值之和 的问题。
那么直接用单调栈正着做一次,反着做一次,得到以任意一位为最小值的最大区间的边界,然后用乘法原理计算出对答案的贡献即可。
上代码!
#include
#include
#include
using namespace std;
const int N=1e5+5;
int a[N],b[N];
int n,q;
#define left LEFT
#define right RIGHT
int left[N],right[N];
long long query(int l,int r)
{
if(l>=r) return 0ll;
int len=r-l;
for(register int i=l;i st;
for(register int i=1;i<=len;i++)
{
while(!st.empty()&&b[st.top()]<=b[i]) st.pop();
if(!st.empty()) left[i]=st.top();
else left[i]=0;
st.push(i);
}
st=stack();
for(register int i=len;i>=1;i--)
{
while(!st.empty()&&b[st.top()]>n>>q;
for(register int i=1;i<=n;i++)
cin>>a[i];
while(q--)
{
int l,r;
cin>>l>>r;
cout<
复杂度 \(O(n\times q)\)
虽然看着不小,但已经足以通过此题了。
后记
画图工具:https://www.desmos.com/calculator
求赞 QwQ