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

链接:

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

题解:

我们对每个等差数列维护一棵线段树。

比如等差为 5 , n 为 17 ,则线段树内节点顺序为:

1,6,11,16,2,7,12,17,3,8,13,4,9,14,5,10,15。

然后查询的时候到对应线段树内查询一段就好了。

然后等差太大 (>5) 就不用维护线段树了,数量不会太多(虽然依然不少),直接暴力比较就行了。

实测:

下图由上至下分别维护到了: 8、7、3、4、6、5

代码:

#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;
}
时间: 2024-08-06 20:23:12

【BZOJ3922】Karin的弹幕 线段树&暴力的相关文章

BZOJ 3922 Karin的弹幕 线段树

题目大意 给出一个序列,支持单点修改,每次查询一个位置成等差数列中所有数的最大值. 思路 等差数列如果公差很大的话,那么整个数列中的数并不会很多:但是如果公差很小,我们就可以用线段树来乱搞.具体方法是对于每个公差维护一个线段树,按照对这个公差取模的值来进行划分.这样询问的时候就在一块了. 具体看代码. CODE #define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> #include <iost

HDU 4902 线段树||暴力

给定一个序列,两种操作 1:把一段变成x. 2:把一段每个数字,如果他大于x,就变成他和x的gcd,求变换完后,最后的序列. 线段树解法:用lazy标记下即可,优化方法还是很巧妙的, Accepted 4902 515MS 3308K 1941 B C++ #include "stdio.h" #include "string.h" struct node { int l,r,x;// 在叶子节点代表值,树节点代表成端更新的lazy操作. }data[400010]

HDU 4893 Wow! Such Sequence 线段树暴力

题意:n个数 初始为0,三个操作:给某个数加上d,查询区间和,把区间[l,r]中每个a[i]变为离a[i]最近的斐波那契数,n,m<=1e5. 无操作1情况下,每个点最多变化一次,每次修改logn,算上操作1 最坏情况下修改n+m次 O((n+m)logn). 对区间设个标记 线段树暴力即可. #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1e9+7; const int

【线段树】bzoj3922 Karin的弹幕

设置一个值K. d<=K:建立多组线段树:d>K:暴力. 最优时间复杂度的伪计算: O(n*K*logn(建树)+m*logn(询问类型1)+m*n/K(询问类型2)+m*K*logn(修改)). 求此函数最小值,易得,当K=sqrt(m/logn)时, 时间复杂度:O(m*sqrt(m*logn)). 空间复杂度:O(n*sqrt(m/logn)). 当然,这个计算显然不完全合理,而且,由于使用STL的vector的原因,导致实际建树要慢得多,因此K取得小一些更加合适(跑几组数据自己看看就行

hdu 4288 线段树 暴力 **

题意: 维护一个有序数列{An},有三种操作: 1.添加一个元素. 2.删除一个元素. 3.求数列中下标%5 = 3的值的和. 解题思路: 看的各种题解,今天终于弄懂了. 由于线段树中不支持添加.删除操作,所以题解写的是用离线做法. 我们来看它是如何解决添加.删除的问题的. 首先将所有出现过的数记录下来,然后排序去重,最后根据去重结果建树,然后每个操作数都会对应线段树中的一个点. 遇到添加.删除操作的时候,只要把那个节点的值改变,然后将它对下标的影响处理好就可以. 那么如何处理这些操作对下标的影

HDU 5700 区间交 线段树暴力

枚举左端点,然后在线段树内,更新所有左边界小于当前点的区间的右端点,然后查线段树二分查第k大就好 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; const int N = 100005; LL a[N]; struct Node{ int l,r; bool operator<(const Node &

魔性の分块 | | jzoj1243 | | 线段树の暴力

题目的打开方式是酱紫的 然而作为一只蒻蒟根本不会线段树该怎么办呢? sro  MZX  orz 是这样说的:用分块啊! 分块 根据紫萱学姐的教程,分块的打开姿势是这样的: 我们要对一个数组进行整体操作,那么我们就可以把他们分成元素相等的n部分,由于n部分的最大值很容易找,也就是我们可以预处理出最大值(更何况此题最开始数组赋初值为0)我们要查询第k1-k2个数的最大值,就返回k1-k2所对应的块的最大值即可, 好,作为一只蒻蒟根本不知所云? 用一张图解来表示一下就是酱紫 红线代表整个数组,底下数轴

3038: 上帝造题的七分钟2 [线段树 暴力]

3038: 上帝造题的七分钟2 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1210  Solved: 536[Submit][Status][Discuss] Description XLk觉得<上帝造题的七分钟>不太过瘾,于是有了第二部."第一分钟,X说,要有数列,于是便给定了一个正整数数列.第二分钟,L说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作.第三分钟,k说,要能查询,于是便有了求一段数的和的操作.第四分

hdu1394线段树点修改,区间求和

Problem Description The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj. For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of