601B:Lipshitz Sequence
题意简述
给定
n
个元素的数列
定义
f(x,y)=max(⌈|hi−hj|j−i⌉)(x≤i≤j≤y)
。
给出
q
个询问,形如
求这个区间
∑f(i,j)(l≤i≤j≤r)
。
数据范围
1≤n≤105
1≤q≤100
1≤hi≤108
思路
询问非常少…对吧?
然而并没有什么卵用…
手玩了一下样例,发现所有的
f(x,y)
的取值必然出现在
|hi−hi+1|
之中。
求出一个差分数组
ai=|hi−hi+1|
预处理ST表,预处理出每一个
ai
向左向右最大
<ai
<script type="math/tex" id="MathJax-Element-14">
每一个
ai
的贡献就是
(left+1)∗(right+1)∗ai
处理每一个询问就扫一遍区间内的所有数。
好吧这就是
q
很小的原因。
复杂度
总结:任何题目一定都是利用它的某些性质,得到优秀的复杂度的。严谨的证明很重要,第一感觉和手玩也很重要!
代码
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
using namespace std;
long long ans;
int n,q,u,v;
int seq[100010],a[100010];
int st[100010][20],leftMin[100010],rightMax[100010];
void preST()
{
for (int i=1;i<n;i++)
st[i][0]=a[i];
int k=int(log2(n));
for (int j=1;j<=k;j++)
for (int i=1;i<n;i++)
if (i+(1<<j-1)<n)
st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
void preLR()
{
int k=int(log2(n));
for (int i=1;i<n-1;i++)
{
int tmp=i+1;
for (int j=k;j>=0;j--)
if (tmp+(1<<j)-1<n)
if (st[tmp][j]<=a[i])
rightMax[i]+=1<<j,tmp+=1<<j;
}
for (int i=2;i<n;i++)
{
int tmp=i-1;
for (int j=k;j>=0;j--)
if (tmp-(1<<j)+1>=1)
if (st[tmp-(1<<j)+1][j]<a[i])
leftMin[i]+=1<<j,tmp-=1<<j;
}
}
int main()
{
scanf("%d%d",&n,&q);
for (int i=1;i<=n;i++)
scanf("%d",&seq[i]);
for (int i=1;i<n;i++)
a[i]=abs(seq[i+1]-seq[i]);
preST();
preLR();
for (int i=1;i<=q;i++)
{
scanf("%d%d",&u,&v);
ans=0;
for (int j=u;j<v;j++)
ans+=1LL*(min(leftMin[j],j-u)+1)*(min(rightMax[j],v-j-1)+1)*a[j];
printf("%I64d\n",ans);
}
return 0;
}