JZOJ 5703. 【gdoi2018 day2】木板(board)

Description

Description

Input

Input

Output

Output

Sample Input

4 4
1 2 5 6
1 2 4 2
1 1 2
1 3 4
1 1 4
1 1 3

Sample Output

1
2
8
6

Data Constraint

Data Constraint

Solution

  • 这题一开始时以为是数据结构,但是想了很久都没什么想法,结果原来是链表暴力跳!!!

  • 做这题充分利用数据随机这个重要条件!

  • l[i],r[i] l [ i ] , r [ i ] 分别表示点 i i 向左向右看比自己高的位置在哪儿。

  • 这个可以预处理出来,比如说算 l[i] ,就可以用个指针 j=i1 j = i − 1 ,不断比, j=l[j] j = l [ j ]

  • 不断跳以找到符合条件的 j j ,再 l[i]=j

  • 由于数据随机,跳的次数约为 log l o g ,故预处理的复杂度为 O(N log N) O ( N   l o g   N )

  • 接着我们来处理修改,显然修改横坐标是没用的,直接数组赋值就好了。

  • 而改高度的话,就可能会变动一些 l,r l , r ,我们考虑暴力维护。

  • 现用上述方法重新求出修改点 x x l[x],r[x]

  • 比如说我们要重新维护一些点的 r r ,那么显然只有第 l[x] x1 x − 1 个点需要暴力修改。

  • 那还等什么,暴力改呗!由于数据随机,修改的复杂度约为 O(log N) O ( l o g   N ) O(log2N) O ( l o g 2 N )

  • 而改 l l 也同理,就是从 x+1 r[x] r [ x ] 要暴力修改。

  • 最后就是最重要的询问了。

  • 我们可以利用单调性来解决问题!

  • 设区间为 [x,y] [ x , y ] ,那么当沿着 x x r y y l 一直跳,

  • 那么跳出来的点高度一定是先单调上升、再单调下降的。

  • 那么贪心一下,令 l1,r1 l 1 , r 1 分别沿着单调上升、单调下降的点跳,

  • 先保证 l1 l 1 的高度不比 r1 r 1 高,扫一遍更新答案。

  • 之后再使保证 r1 r 1 的高度不比 l1 l 1 高,再扫一遍更新答案。

  • 由于数据随机,这些点的数量约为 log 个 ,故查询的复杂度约为 O(log N) O ( l o g   N )

  • 那么总时间复杂度就约为 O(N log N) O ( N   l o g   N ) ,竟轻松通过本题!!!

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=5e5+5;
struct data
{
    int x,y;
}a[N];
int l[N],r[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline LL max(LL x,LL y)
{
    return x>y?x:y;
}
int main()
{
    freopen("board.in","r",stdin);
    freopen("board.out","w",stdout);
    int n=read(),q=read();
    for(int i=1;i<=n;i++) a[i].x=read();
    for(int i=1;i<=n;i++) a[i].y=read();
    for(int i=1;i<=n;i++)
    {
        int pos=i-1;
        while(pos && a[pos].y<=a[i].y) pos=l[pos];
        l[i]=pos;
    }
    for(int i=n;i;i--)
    {
        int pos=i+1;
        while(pos<=n && a[pos].y<=a[i].y) pos=r[pos];
        r[i]=pos;
    }
    while(q--)
    {
        int ty=read(),x=read(),y=read();
        if(ty==3) a[x].x=y; else
        if(ty==2)
        {
            a[x].y=y;
            int pos=x-1;
            while(pos && a[pos].y<=a[x].y) pos=l[pos];
            l[x]=pos;
            pos=x+1;
            while(pos<=n && a[pos].y<=a[x].y) pos=r[pos];
            r[x]=pos;
            for(int i=x-1;i>=l[x];i--)
            {
                pos=i+1;
                while(pos<=n && a[pos].y<=a[i].y) pos=r[pos];
                r[i]=pos;
            }
            for(int i=x+1;i<=r[x];i++)
            {
                pos=i-1;
                while(pos && a[pos].y<=a[i].y) pos=l[pos];
                l[i]=pos;
            }
        }else
        {
            LL ans=0;
            for(int i=x,j=y;i<=y;i=r[i])
            {
                while(j>i && a[i].y>a[j].y) j=l[j];
                if(j<=i) break;
                ans=max(ans,(LL)(a[j].x-a[i].x)*a[i].y);
            }
            for(int i=y,j=x;i>=x;i=l[i])
            {
                while(j<i && a[i].y>a[j].y) j=r[j];
                if(j>=i) break;
                ans=max(ans,(LL)(a[i].x-a[j].x)*a[i].y);
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值