zoukankan      html  css  js  c++  java
  • 「Codeforces 601B」 Lipshitz Sequence

    Description

    定义数列 (h[1..n])Lipschitz 常数为:

    [operatorname{L}(h)=egin{cases} 0&n=1\ maxlimits_{1le j< ile n} {leftlceildfrac{|h[j]-h[i]|}{j-i} ight ceil}&nge 2 end{cases}]

    现给定一个长度为 (n) 的数列 (h[1..n]) 以及 (q) 个询问。

    每个询问给出一个二元组 ((l,r)) ,要求对于每组询问求出所有子序列的 Lipschitz 常数之和,即求 :

    [sumlimits_{lle ile jle r} operatorname{L}(h[i..j]) ]

    Solution

    请务必耐心看完切理解题面。

    显然不是暴力可以随随便便搞出来的。

    当然你也可以使用 ST 表或者线段树,不过这里介绍的是一种最简单的方法,效率也丝毫不亚于上面二者的方法——单调栈。


    首先我们有一个命题,这是我们解题的关键。

    • 命题:数列 (a[x..y])Lipschitz 常数必定是由数列中 相邻的两项 计算而来。即 (operatorname{L}(a[x..y])=maxlimits_{xle i<y}leftlceildfrac{|a_i-a_{i+1}|}{2} ight ceil)

    接下来给出证明(简单的解释而已,可能不太漂亮):

    不妨将 ((i,a[i])) 视作平面上的一点,这样我们得到 (operatorname{size}(a)) 个点,从而将原命题转化为了这样一个新命题:在上述所有的点中任选两点构成的直线斜率的绝对值之中,最大的必定是由横坐标之差为 (1) 的两点(即相邻的)构成。

    这个命题相比之前一个要好证很多。我们取这 (operatorname{size}(a)) 个点中任意三个,令其为 (A,B,C(x_A<x_B<x_C,y_A<y_B<y_C))。(这里只是一种情况,其他情况同理)。

    1. (k_{AB}<k_{BC}) 的情况:

    3V38W4.png

    图中可见, (k_{BC}>k_{AC})

    1. (k_{AB}>k_{BC})

    3V8U3Q.png

    (从左至右分别为点 (A,B,C)

    图中可见, (k_{AB}>k_{AC})

    再将其他情况手玩一下,可以发现,最大的斜率一定为相邻的两点所构成的直线的斜率。

    于是我们可以很自然地转化 (operatorname{L}(h[l..r])) 为:(maxlimits_{lle ile r} leftlceildfrac{|h_{i+1}-h_i|}{2} ight ceil)

    (b_{i}=|h_{i+1}-h_{i}|)

    于是原问题就成了求:

    [sumlimits_{lle i<j<r}operatorname{L}(h[i..j])=sumlimits_{lle i<j< r}maxlimits_{ile k< j} leftlceildfrac{b_k}{2} ight ceil ]


    行了,经过以上的 瞎扯 ,问题已经原型毕露了。

    可以看出这实质上就是一个 一段区间的所有子区间最大值之和 的问题。

    那么直接用单调栈正着做一次,反着做一次,得到以任意一位为最小值的最大区间的边界,然后用乘法原理计算出对答案的贡献即可。

    复杂度为 (O(n))

    上代码!

    Code

    /*
     * Author : _Wallace_
     * Source : https://www.cnblogs.com/-Wallace-/
     * Problem : Codeforces 601B Lipshitz Sequence
     */
    #include<iostream>
    #include<stack>
    #include<cmath>
    using namespace std;
    
    const int N=1e5+5;
    int a[N],b[N];
    int n,q;
    
    #define left LEFT
    #define right RIGHT
    int left[N],right[N];
    long long query(int l,int r)
    {
    	if(l>=r) return 0ll;
    	int len=r-l;
    	for(register int i=l;i<r;i++)
    		b[i-l+1]=abs(a[i+1]-a[i]);
    	stack<int> st;
    	for(register int i=1;i<=len;i++)
    	{
    		while(!st.empty()&&b[st.top()]<=b[i]) st.pop();
    		if(!st.empty()) left[i]=st.top();
    		else left[i]=0;
    		st.push(i);
    	}
    	st=stack<int>();
    	for(register int i=len;i>=1;i--)
    	{
    		while(!st.empty()&&b[st.top()]<b[i]) st.pop();
    		if(!st.empty()) right[i]=st.top();
    		else right[i]=len+1;
    		st.push(i);
    	}
    	long long ret=0ll;
    	for(register int i=1;i<=len;i++)
    		ret+=b[i]*1ll*(i-left[i])*(right[i]-i);
    	return ret;
    }
    
    signed main()
    {
    	cin>>n>>q;
    	for(register int i=1;i<=n;i++)
    		cin>>a[i];
    	while(q--)
    	{
    		int l,r;
    		cin>>l>>r;
    		cout<<query(l,r)<<endl;
    	}
    	return 0;
    }
    

    复杂度 (O(n imes q))

    虽然看着不小,但已经足以通过此题了。


    Notes

    画图工具:https://www.desmos.com/calculator

  • 相关阅读:
    Python3 字典Dict(十三)
    Python3 元组Tuple(十二)
    Python3 列表List(十一)
    Python3 循环语句(十)
    Python3 条件控制(九)
    Python3 运算符(八)
    Swift3.0语法2
    Swift反射机制实现 AppDelegate 字符串获取类并成为根控制器
    Swift语法(更新)
    单例
  • 原文地址:https://www.cnblogs.com/-Wallace-/p/12337827.html
Copyright © 2011-2022 走看看