GDOI2018 D2T3 木板 (性质: 随机数列的递增数列长度为log)

86 篇文章 0 订阅
28 篇文章 0 订阅

递增#题意
给定n个二元组(xi,yi),两个二元组i和j的价值定义为|xi−xj|∗max{yi,yj}
一个区间[L,R]的价值是其中任选两个二元组的最大价值。
给定m个操作,修改一个二元组的x或y,询问一个区间的价值。
n,m<=10^5。
数据保证初始的y和修改的y随机生成。

有理有据期望复杂度

考虑一个1..n的随机排列,他与一棵笛卡尔树唯一双向映射。
相当于在笛卡尔树的所有形态中随机选取,因此树高与随机二叉树相同,都是期望log.

根据笛卡尔树树高log这个结论,可以得出递减序列长度也期望是log.(取最左最右,往上走可得)

维护左右第一个比它大的。
查询,发现有用的点一定在递增递减序列上。也就是log个,根据单调性log求一下max.

修改a,可以发现有改动的点一定在a-1的左链上,a+1的右链上。
根据单调性调整一下lmax,rmax即可。
时间复杂度: n log n

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int N=5e5+10;

int n,q;
ll x[N],y[N],lm[N],rm[N];
int Q[N];
int main() {
    freopen("board.in","r",stdin);
    freopen("board.out","w",stdout);
    cin>>n>>q;
    for (int i=1; i<=n; i++) scanf("%lld",&x[i]);
    for (int i=1; i<=n; i++) scanf("%lld",&y[i]);

    for (int i=1; i<=n; i++) {
        while (Q[0] && y[Q[Q[0]]]<=y[i]) {
            rm[Q[Q[0]]]=i;
            Q[0]--;
        }
        Q[++Q[0]]=i;
    }

    Q[0]=0;
    for (int i=n; i; i--) {
        while (Q[0] && y[Q[Q[0]]]<=y[i]) {
            lm[Q[Q[0]]]=i;
            Q[0]--;
        }
        Q[++Q[0]]=i;
    }

    for (int i=1; i<=q; i++) {
        ll ty,a,b; scanf("%lld %lld %lld",&ty,&a,&b);
        if (ty==1) {
            ll g=a,d=b,ans=0;
            for (;g && g<=b;g=rm[g]) {
                while (d && d>=a && y[d]<y[g]) d=lm[d];
                if (d==0 || g>=d) break;
                ans=max(ans,(x[d]-x[g])*y[g]);
            }
            g=a,d=b;
            for (;d>=a;d=lm[d]) {
                while (g && g<=b && y[g]<y[d]) g=rm[g];
                if (g==0 || g>=d) break;
                ans=max(ans,(x[d]-x[g])*y[d]);
            }
            printf("%lld\n",ans);

        } else if (ty==2) {
            y[a]=b;
            lm[a]=rm[a]=0;
            for (int i=a-1; i; i=lm[i]) if (y[i]>y[a]) {
                lm[a]=i; break;
            }

            for (int i=a+1; i; i=rm[i]) if (y[i]>y[a]) {
                rm[a]=i; break;
            }

            int now=a+1;
            for (int i=a-1; i; i=lm[i]) {
                while (now && y[now]<=y[i]) now=rm[now];
                rm[i]=y[a]>=y[i] ? a : now;
            }
            now=a-1;
            for (int i=a+1; i; i=rm[i]) {
                while (now && y[now]<=y[i]) now=lm[now];
                lm[i]=y[a]>=y[i] ? a : now;
            }
        } else x[a]=b;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值