L. Two Buildings(决策单调性 )详解

2020-2021 ACM-ICPC, Asia Seoul Regional Contest 补题

题目传送门
朴素算法(暴搜):o( n 2 n^2 n2)
决策单调:o(n* log ⁡ 2 n \log_2^n log2n)

题意:

给一长度n(n<=1e6)的数组, h i h_i hi<=1e6,求max( h i h_i hi+ h j h_j hj)∗(j−i) 1≤i<j≤n.

思路:

直接求很难,进行公式变换 ( h j h_j hj - ( - h i h_i hi ) )∗(j−i) 1≤i<j≤n.
之后就转换成求在直角坐标系里求 矩形面积的问题。 ( j, h j h_j hj ) ( i, h i h_i hi )
这个时候决策单调性优化DP就派上用场了。

决策单调性:

啥?啥?啥? 啥是决策单调性?
就是说,对于任意a<b<c<d,若满足在c处转移到b比a优,那么在d处也满足。

分析:

对于此题怎么做呢?
首先需要有一个结论(其实也显而易见),假如对于一个点i来说,它的最佳点在j(i < j),那么对于任意一个点p(p < i),p的最佳点一定<=j。
先创建两个数组: a,b数组, 以下标为x坐标,值为y坐标。并且规定a数组点在第一象限,b数组在第四象限(但b数组的值还是正数,后会有图)

得到a,b数组

在这里插入图片描述

( h j h_j hj - ( h i h_i hi ) )∗(j−i) 1≤i<j≤n.
对于一个固定的(j,a[j]),选择的(i,b[i])显然位置右下越优.
那么对于(i,b[i])和(j,b[j]),如果i<j且b[i]>b[j],
那么(i,b[i])完全可以删去,所以可以的b数组。
eg: 一串数 1 3 2 5 4
遍历
我这里是以1点固定,看他右边的点(也就是3点)。(对角线确定一个矩形)
对于b数组的情况:
固定1号点,找3号点。
先看见1 后面没有比他更小的 所以b: 1
下一个是3,后面的数字有比3小的,所以舍去 此时b:1
下一个是2,后面的数字没有2还小的,所以留下 此时b:1 2
下一个是5,后面的数字有比5小的数字;所以舍去: 此时b: 1 2
下一个是4, 后面的数字,无了, 所以留下, 此时: 1 , 2, 4;
所以得到的b数组值:1, 2,4。
发现没, b数组是单调的,递增.
具体为什么呢:每次是否留下这个数字:都要看后面有没有更小的,有就舍去了,没有就留下,所以说后面的数字都比该数字要大。

对于a组数的情况同理:
得到是:1 3 5. (也是单调的).
可能会有些迷惑为什么?
是这样的,前面分析b数组把1号点固定,所以这里分析得到a数组就要把3号点固定。
(ps:那我前面分析a数组是固定4号点,也是可以的,那么这时分析得到b数组就要为固定2号点,当然得到的a,b数组值就会不同)
这里我是以1号,3号点形成对角线来分析的。(这里是为统一规则).
来吧~
固定3号点,找1号点;
先看见4,前面的数字有比4大的点,所以舍去 ,当前a数组:无。(为什么舍去呢,以为前面的数字5,形成的面积显然比他大)。
然后看见5,前面的数字没有比大的,所以留下, 当前a数组:5.
然后看见2,前面的数字有比2大的点,所以舍去 ,当前a数组:5.
然后看见3,前面的数字没有比大的,所以留下, 当前a数组:3,5.
之后看见1,前面没有数字可比较,所以留下:当前a数组:1,3,5;
最终得到a数组为1,3,5.

构造递归,关键。

1.在a数组中,找出中间的点,mid (如下图)
2.找出以mid点(1号点) 围成最大的面积, 令找出来的点为pos.
3.关键来了!!! 那么对mid左边的点 能找出最大面积的点 一定在pos左边或是pos点。
mid右边的点,能找出最大面积的点一定在pos的右边或是pos点

这里 为方便表示 s(i,j) 对角表示矩形面积。
对mid左边的点 能找出最大面积的点 一定在pos左边或是pos点。
为什么?
这里我们使用反证法,就是说明pos以右的点都比pos点小即可。
目标就是证明:s(i,pos) >s(i,j)成立即可。
前面的前提说了 s(mid,pos)> s(mid,j)
所以 s(mid,pos)与s(mid,j)面积 不重合的部分:有①>②+③;
对于:s(i,pos) 与s(i,j)面积不重合的部分:有:①+④>②;
所以显然:s(i,pos) >s(i,j) 成立。 得证完毕。
我觉得吧!人生总要留点遗憾才算完美。
这里的证明不再复述,同理:mid右边的点,能找出最大面积的点一定在pos的右边(举一反三,思路是一样的).
在这里插入图片描述
所以我们要求最大面积就是三者之间,mid左边 mid mid右边。 mid左右边接着套娃呗~
代码如下(配有注释):

#include<iostream>
#include<algorithm>
#include<math.h>
#include<cstring>
#include<vector>
#include<queue>
#define ll long long
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=1e6+10;
const ll mod=1e9+7;
ll read(){
    ll s = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
    return s * f;
}
int n;
ll a[N], b[N],h[N]; 
int cnta, cntb;
ll calc(int i,int j){  // 传入下标  求面积 
    return (h[a[i]]+h[b[j]])*(b[j]-a[i]);
}
using namespace std;
ll solve(int l1,int r1,int l2,int r2){
   if(l1>r1||l2>r2)  
       return 0;
    int mid = (l1 + r1) >> 1;
    int pos = l2;  
     ll s = calc(mid,l2);
    for (int i = l2+1; i <= r2;i++){
        if(a[mid]<b[i]){
            ll ans = calc(mid, i);
            if(s<ans){
            	//找出最优的pos点. 
                s = ans;
                pos = i;
            }
        }
    }
     //比较mid左边 mid mid右边 的最大值 
    s = max(s,solve(l1, mid-1, l2, pos));
    s = max(s,solve(mid+1, r1, pos, r2));
    return s;
}
int main (){
    n = read();
    for (int i = 1; i <= n;i++)
        h[i] = read();
        //这里的a,b数组存放的都是h数组的下标,与上文的存值不同,但是一样
		//存下标是为了方便 二分嘛。 
    for (int i = 1; i <= n;i++){
        if(cnta==0)
            a[++cnta] = i;
        else if(h[i]>h[a[cnta]])
            a[++cnta] = i;
    }
    
    for (int i = n; i >=1;i--){
        if(cntb==0)
            b[++cntb] = i;
        else if(h[i]>h[b[cntb]])
            b[++cntb] = i;
     }
	  //b数组逆转一下就好了。 
     reverse(b + 1, b + 1 + cntb);
	/* for(int i=1;i<=cntb;i++){
     	cout<<h[b[i]]<<" ";
	 } */
     cout<<solve(1,cnta,1,cntb);
     return 0;
}

感谢观看!!
都看到最后了,点个赞呗~(doge)

参考资料:

风浔凌:2020-2021 ACM-ICPC, Asia Seoul Regional Contest 补题|题解
live4m:gym102920 L. Two Buildings(决策单调性+分治)

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

axtices

谢谢您的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值