CodeForces 601B Lipshitz Sequence (单调队列)

来源:互联网 发布:语音广告合成软件 编辑:程序博客网 时间:2024/06/05 23:58

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

由于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