【BZOJ】3922: Karin的弹幕

本文介绍了一种解决等差子序列最大值查询问题的方法,通过设定阈值并结合线段树与暴力搜索策略,实现高效查询与单点修改操作。详细解析了算法流程与复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

给定一个长度为\(n(1 \le n \le 70000)\)序列,\(m(1 \le m \le 70000)\)次操作:1. 对一段下标是等差数列的子序列求最大值;2. 单点修改。

分析

如果公差很大,那么速度是很快的。所以我们考虑阈值。

题解

设阈值\(k\)表示如果询问的公差小于等于\(k\)则用线段树求,否则暴力。
则我们对阈值每一个公差\(i(1 \le i \le k)\)\(i\)棵线段树。
复杂度\(O(kn + m(\frac{n}{k} + log n))\)

#include <bits/stdc++.h>
using namespace std;
const int N=70005, MXD=10, oo=(~0u>>1)+1;
int a[N], D, sz[MXD+1][MXD+1], b[N];
struct node *null;
struct node {
    node *c[2];
    int mx;
    node() {
        c[0]=c[1]=0;
        mx=oo;
    }
    void up() {
        mx=max(c[0]->mx, c[1]->mx);
    }
}Po[10000005], *iT=Po, *root[MXD+1][MXD+1];
node *newnode() {
    return iT++;
}
node* build(int l, int r) {
    node *x=newnode();
    if(l==r) {
        x->mx=b[l];
        return x;
    }
    int mid=(l+r)>>1;
    x->c[0]=build(l, mid);
    x->c[1]=build(mid+1, r);
    x->up();
    return x;
}
int query(int L, int R, int l, int r, node *x) {
    if(L<=l && r<=R) {
        return x->mx;
    }
    int mid=(l+r)>>1, ret=oo;
    if(L<=mid) {
        ret=query(L, R, l, mid, x->c[0]);
    }
    if(mid<R) {
        ret=max(ret, query(L, R, mid+1, r, x->c[1]));
    }
    return ret;
}
void update(int p, int go, int l, int r, node *x) {
    if(l==r) {
        x->mx+=go;
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid) {
        update(p, go, l, mid, x->c[0]);
    }
    else {
        update(p, go, mid+1, r, x->c[1]);
    }
    x->up();
}
void init(int n) {
    D=min(MXD, n);
    for(int d=1; d<=D; ++d) {
        for(int i=1; i<=d; ++i) {
            int &s=sz[d][i];
            for(int j=i; j<=n; j+=d) {
                b[++s]=a[j];
            }
            root[d][i]=build(1, s);
        }
    }
}
int query(int x, int d, int n) {
    if(d>D) {
        int mx=oo;
        for(int i=x; i<=n; i+=d) {
            mx=max(mx, a[i]);
        }
        return mx;
    }
    int begin=(x-1)%d+1, pos=(x-1)/d+1, len=sz[d][begin];
    return query(pos, len, 1, len, root[d][begin]);
}
void update(int x, int y) {
    a[x]+=y;
    for(int d=1; d<=D; ++d) {
        int begin=(x-1)%d+1, pos=(x-1)/d+1, len=sz[d][begin];
        update(pos, y, 1, len, root[d][begin]);
    }
}
int main() {
    int n, m;
    scanf("%d", &n);
    for(int i=1; i<=n; ++i) {
        scanf("%d", &a[i]);
    }
    init(n);
    scanf("%d", &m);
    while(m--) {
        int op, x, y;
        scanf("%d%d%d", &op, &x, &y);
        if(op) {
            printf("%d\n", query(x, y, n));
        }
        else {
            update(x, y);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/iwtwiioi/p/4986313.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值