Code Forces 601 B. Lipshitz Sequence(单调栈)

Description
根据利普希茨条件定义一个长度为n的序列h的L(h)值:
n<2,L(h)=0;
n>=2,这里写图片描述(1<=i< j<=n)
现给出一长度为n的序列a,给出q次查询,每次查询给出一个区间[l,r],求序列s={a[l],…,a[r]}的所有子序列L值之和
Input
第一行为两个整数n和q表示序列长度和查询次数,第二行n个整数ai表示这个序列,之后q行每行两个整数l和r表示查询区间(2<=n<=1e6,1<=q<=100,0<=ai<=1e8)
Output
对于每次查询,输出L(s)的值
Sample Input
10 4
1 5 2 9 1 3 4 2 1 7
2 4
3 8
7 10
1 9
Sample Output
17
82
23
210
Solution
设x1< x2< x3,y1< y2< y3,下面证明
这里写图片描述
不妨设这里写图片描述,那么我们有
这里写图片描述
这里写图片描述
所以一个序列的L(h)=max( |h[i+1]-h[i]| )(1<=i< n),那么若求一个序列所有子序列L值之和,只需求出每个|h[i+1]-h[i]|的影响范围即可[l,r](即在这个区间内|h[i+1]-h[i]|最大),那么用单调栈即可解决这类问题
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 111111
typedef long long ll;
int n,q,a[maxn],b[maxn],s[maxn],top,L[maxn],R[maxn],l,r,len;
int main()
{
    while(~scanf("%d%d",&n,&q))
    {
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        while(q--)
        {
            scanf("%d%d",&l,&r);
            len=r-l;
            for(int i=l;i<r;i++)
                b[i-l+1]=abs(a[i]-a[i+1]);
            top=0;
            for(int i=1;i<=len;i++)
            {
                while(top&&b[i]>=b[s[top]])top--;
                L[i]=top==0?0:s[top];
                s[++top]=i;
            }   
            top=0;
            for(int i=len;i>=1;i--)
            {
                while(top&&b[i]>b[s[top]])top--;
                R[i]=top==0?len+1:s[top];
                s[++top]=i;
            }
            ll ans=0;
            for(int i=1;i<=len;i++)
                ans+=1ll*(i-L[i])*(R[i]-i)*b[i];
            printf("%I64d\n",ans);
        }
    }
    return 0;
}

你可能感兴趣的:(Code Forces 601 B. Lipshitz Sequence(单调栈))