【BZOJ3922】Karin的弹幕 线段树&暴力

本文介绍了一种利用线段树优化等差数列查询的方法,通过维护多个线段树来加速不同等差间隔的数据查询过程。当等差较大时采用直接比较的方式进行优化。

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

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.youkuaiyun.com/vmurder/article/details/44978599");
}

题解:

我们对每个等差数列维护一棵线段树。
比如等差为 5n17 ,则线段树内节点顺序为:
1611162712173813491451015

然后查询的时候到对应线段树内查询一段就好了。
然后等差太大 (>5) 就不用维护线段树了,数量不会太多(虽然依然不少),直接暴力比较就行了。

实测:

下图由上至下分别维护到了: 873465
Karin弹幕维护等差时间实测图

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 300000
#define M 5
#define ls (note<<1)
#define rs (note<<1|1)
#define inf 0x3f3f3f3f
using namespace std;
int src[N],n,m;
struct Segment_Tree
{
    int s[N],st[N],en[N],crs[N],t[N];
    void pushup(int note)
    {
        s[note]=max(s[ls],s[rs]);
    }
    void build(int note=1,int l=1,int r=n)
    {
        if(l==r)
        {
            s[note]=t[l];
            crs[l]=note;
            return ;
        }
        int mid=l+r>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        pushup(note);
    }
    void init(int d)
    {
        int p=0;
        for(int i=1;i<=d;i++)
        {
            for(int k=i;k<=n;k+=d)
            {
                st[k]=++p;
                t[p]=src[k];
            }
            for(int k=i;k<=n;k+=d)en[k]=p;
        }
        build();
    }
    void add(int x,int w)
    {
        s[x=crs[st[x]]]+=w;
        for(x>>=1;x;x>>=1)pushup(x);
    }
    int query(int l,int r,int note=1,int L=1,int R=n)
    {
        if(L==l&&r==R)return s[note];
        int mid=L+R>>1;
        if(r<=mid)return query(l,r,ls,L,mid);
        else if(l>mid)return query(l,r,rs,mid+1,R);
        else return max(query(l,mid,ls,L,mid),query(mid+1,r,rs,mid+1,R));
    }
    int ask(int x){return query(st[x],en[x]);}
}s[M+1];
int main()
{
//  freopen("test.in","r",stdin);

    int i,j,k;
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%d",&src[i]);
    for(i=1;i<=M;i++)s[i].init(i);
    for(scanf("%d",&m);m--;)
    {
        scanf("%d%d%d",&i,&j,&k);
        if(i==0)
        {
            src[j]+=k;
            for(i=1;i<=M;i++)s[i].add(j,k);
        }
        else {
            if(k==0)printf("%d\n",src[j]);
            else if(k>M)
            {
                int ans=-inf;
                while(j<=n)ans=max(ans,src[j]),j+=k;
                printf("%d\n",ans);
            }
            else printf("%d\n",s[k].ask(j));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值