zoukankan      html  css  js  c++  java
  • Codeforces 601B(贪心+斜率+组合数学+单调栈)

    题面

    传送门
    题目大意:
    L(h)的值是区间[L,R]内,abs(h[i]-h[j])/(i-j)的最大值。现在有q个询问,每个询问表示询问区间[L,R]内,所有子序列的L(h)的值的和

    分析

    |h[i]h[j]ij|想成斜率,显然选相邻的两个数最优,最大的斜率只会存在于相邻两点
    所以我们可以预处理所有h[i]-h[i-1]的值,记作d[i]
    问题转化为求[L,R]中每个子区间中的最大d[i]之和

    朴素算法是枚举所有子区间,时间复杂度O(n2),显然会TLE
    因此我们可以计算每个d[i]被算了多少次
    具体方法如下:
    维护一个单调栈,从左到右依次将d[i]入栈,栈顶元素最小
    那么过程中,当区间左端点<=i-1时,区间最大值为弹出栈顶的元素
    当区间右端点>=i+1时,区间最大值为新加入进去的元素
    所谓x的作用,就是区间端点在a左侧或在b右侧时,区间内的最大值为x,此时便是”有作用”的,因为会被算进结果里
    如d={2,5,3,9,8,1}
    将5入栈时弹出2,则区间右端点小于等于1时,区间最大值为2
    当区间左端点>=2时,区间最大值为5 (当然此时还没有计算出右端点,之后9入栈可以计算出右端点<=3时区间最大值为5)

    设lbound[i],rbound[i]表示区间左端点>=lbound[i]且区间右端点<=rbound[i]时区间最大值为d[i],查询区间为[L,R]
    则由乘法原理得,d[i]被计算了(i-lbound[i]+1)*(rbound[i]-i+1)次
    于是对于每一个i,我们可以O(1)的时间内求出结果

    单调栈预处理时间复杂度O(n),查询时间复杂度为O(nq)
    一些陷阱:

    1.数据范围:最后答案要用long long
    2.单调栈最后里面元素要出栈,它们的rbound值为n
    3.若单调栈为空,新插入的元素的lbound值为1

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<stack> 
    #include<algorithm>
    #include<cmath>
    #define maxn 100005 
    #define ForMyLove return 0;
    using namespace std;
    int n,q;
    int a[maxn];
    int lbound[maxn],rbound[maxn]; 
    long long d[maxn];
    struct node{
        int pos;
        int value;
        node(){
    
        }
        node(int i,int x){
            pos=i;
            value=x;
        }
    }; 
    stack<node>s;
    int main(){
        int l,r;
        scanf("%d %d",&n,&q);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++) d[i]=abs(a[i]-a[i-1]);
        for(int i=1;i<=n;i++){
            node now;
            while(!s.empty()){
                now=s.top();
                if(d[i]>now.value){
                    rbound[now.pos]=i-1;
                    s.pop();
                }else break;
            }
            if(s.size()==0) lbound[i]=1;
            else{
                now=s.top();
                lbound[i]=now.pos+1;
            }
            s.push(node(i,d[i]));
        }
        while(!s.empty()){
            node now=s.top();
            rbound[now.pos]=n;
            s.pop();
        }
        while(q--){
            scanf("%d %d",&l,&r);
            long long ans=0;
            for(int i=l+1;i<=r;i++){
                long long left=max(l+1,lbound[i]);
                long long right=min(r,rbound[i]);
                ans=ans+(i-left+1)*(right-i+1)*d[i];
            }
            printf("%I64d
    ",ans);
        }
        ForMyLove
    } 
  • 相关阅读:
    附近有什么?8款可以查周边的App
    实体店里充话费要怎么弄
    怎样买手机号?
    手机号是SIM卡的号呢,还是买手机时就带的
    网站SSL证书在线检测
    未来什么行业最赚钱
    陈安之-如何选择最赚钱的行业
    斗鱼宣布获C轮15亿融资 直播行业进入资本时代
    2016年Godaddy最新域名转出教程
    GoDaddy账户间域名转移PUSH以及ACCEPT接受域名过户方法
  • 原文地址:https://www.cnblogs.com/birchtree/p/9858041.html
Copyright © 2011-2022 走看看