zoukankan      html  css  js  c++  java
  • 【题解】Diferenc-Diferencija [SP10622]

    【题解】Diferenc-Diferencija [SP10622]

    传送门:( ext{Diferenc-Diferencija}) ( ext{[SP10622]})

    【题目描述】

    序列的值被定义成其中最大的元素减去最小的元素。如序列 ((3,1,7,2)) 的值为 (7-1=6), 序列 ((42,42)) 的值为 (42-42=0)

    现给定一长为 (n) 的序列 (a),求出所有连续子序列的值的和。

    【样例】

    样例输入:
    3
    1
    2
    3
    样例输出:
    4
    
    样例输入:
    4
    7
    5
    7
    5
    样例输出:
    12
    
    样例输入:
    4
    3
    1
    7
    2
    样例输出:
    31
    

    【数据范围】

    (100 \%:) (2 leqslant n leqslant 3*10^5,) (1 leqslant a[i] leqslant 10^8)


    【分析】

    先将子区间的右端点固定为 (r),此时一共有 (r) 个左端点 ((l in [1,r])) 可与之组成连续子序列,用 (f_1[l]) 表示 (max {a[j]}(j in [l,r]))(f_2[l]) 表示 (min {a[j]}(j in [l,r])) 。于是以 (i) 为右端点 (r) 的子区间贡献和为 (sum_{l=1}^{r} (f_1[l]-f_2[l])),即 (sum_{l=1}^{r} f_1[l] - sum_{l=1}^{r} f_2[l]) 。我们可以分开算 (f_1,f_2) 的总和。

    当右端点移至 (r+1) 时,需要用 (a[r+1]) 来更新 (f_1,f_2),可以直接扫描 ([1,r]),但时间不过不了关。

    随着 (l) 的减小,(f_1[l]=max(a[l],f_1[l+1])),可以发现 (f_1[l]) 是单调不下降的,同理 (f_2[l]) 单调不上升。

    随着 (r) 的增大,(f_1[l]=max(f_1[l],a[r+1])),可以发现 (f_1[l]) 仍是单调不下降的,同理 (f_2[l]) 单调不上升。

    然后我们就会发现一个现象:每次新加进来一个数 (a[r+1]) 时,它会将以 (r+1) 结尾的一段连续的区间 (f_1[l],f_2[l](l in [?,r+1])) 全部赋值为 (a[r+1]),而且被覆盖掉的原数对这之后的区间不再有任何贡献。

    于是我们可以用两个单调栈分别维护 (f_1,f_2),由于下标也是单调递增,所以可以将 (f) 值相同的合并起来,用 (g) 记录 (f) 相同的下标个数,另设一个变量 (S) 表示以 (i) 为右端点的贡献和,当加入新的 (a[r]) 时就不断弹走队尾直至保持单调时结束,同时更新 (S),最后累加答案即可。

    【Code】

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=3e5+2;
    int n,t1,t2,a[N],f1[N],f2[N],g1[N],g2[N];LL S,ans;
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    int main(){
        in(n);
        for(Re i=1;i<=n;++i)in(a[i]);
        f1[++t1]=a[1],f2[++t2]=a[1],g1[t1]=g2[t2]=1,ans=S=0;//初始化入队
        for(Re i=2;i<=n;++i){
            Re tmp=1;//f1[i]和f2[i]都一定会被覆盖,所以初始化为1
            while(t1&&f1[t1]<=a[i])S-=(LL)f1[t1]*g1[t1],tmp+=g1[t1--];//更新最大值
            f1[++t1]=a[i],g1[t1]=tmp,S+=(LL)a[i]*tmp;
            tmp=1;
            while(t2&&f2[t2]>=a[i])S+=(LL)f2[t2]*g2[t2],tmp+=g2[t2--];//更新最小值
            f2[++t2]=a[i],g2[t2]=tmp,S-=(LL)a[i]*tmp;
            ans+=S;//累加答案
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    从B树、B+树、B*树谈到R 树
    1.红黑树和自平衡二叉(查找)树区别 2.红黑树与B树的区别
    红黑树 Java实现
    自平衡二叉(查找树/搜索树/排序树) binary search tree
    从零开始: 二叉查找
    深入理解JAVA虚拟机JVM
    java中的变量各占得字节数
    java中new两个对象,在堆中开辟几个对象空间
    Spring面试,IoC和AOP的理解, @Transactional原理及使用
    EasyUI Tabs + Yii2.0实现iframe方式打开页面(解决共用静态文件引入加载的问题)
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/11627826.html
Copyright © 2011-2022 走看看