CodeForces 601B Lipshitz Sequence (单调队列)

分析:很容易想到,区间斜率绝对值的最大值一定是所有相邻的斜率中的最大值。
然后把序列处理成后一个数减前一个数的绝对值。

由于 q 非常小,所以每一次询问支持o(n)的暴力。找区间 s[l..r] 序列所有子序列的值的和,从左向右枚举每一个数,找这个数对多少个不同区间有贡献,即找出这个数左边离他最近比它大的数的位置,找出右边离他最近的比它大的数的位置。这个时候发现有些区间被重复计算了,比如:如果这个区间有两个最大值,那个 [l..r] 区间被重复了两次。解决这个问题只需要,左边找比这个数大,右边找不小于这个数的,就可以搞定了。

找左边第一个比该数大的值可以通过单调队列来搞定,预处理出所有的 ,L,R 。这题总的复杂度: o(nlogn+qn)

附上代码:

#include <bits/stdc++.h>
#define LL long long
#define FOR(i,x,y)  for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i > y;-- i)

using namespace std;

typedef pair<int,int> pii;

const int maxn = 100010;
const int inf = 1<<30;

int L[maxn],R[maxn];

int q[maxn],head,a[maxn];
pii dif[maxn];

int n,m;

void init(){
    IFOR(i,n-1,0)  a[i] = abs(a[i]-a[i-1]),dif[i] = make_pair(a[i],i);
    dif[0] = make_pair(inf,0);
    head = 0;
    q[head++] = 0;
    FOR(i,1,n){
        while(a[i] >= dif[q[head-1]].first)
            -- head;
        L[i] = dif[q[head-1]].second;
        q[head++] = i;
    }
    dif[n] = make_pair(inf,n);
    head = 0;
    q[head++] = n;
    IFOR(i,n-1,0){
        while(a[i] > dif[q[head-1]].first) -- head;
        R[i] = dif[q[head-1]].second;
        q[head++] = i;
    }
}

void work(){
    FOR(i,0,m){
        int l,r;
        scanf("%d%d",&l,&r);
        -- l; -- r;
        LL ans = 0;
        FOR(i,l+1,r+1){
            LL llen = i - max(L[i],l),rlen = min(R[i],r+1) - i;
            ans += llen*rlen*a[i];
        }
        printf("%I64d\n",ans);
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        FOR(i,0,n)    scanf("%d",&a[i]);
        init();
        work();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值