Luogu 45887 全村最好的嘤嘤刀(线段树 树状数组)

https://www.luogu.org/problemnew/show/T45887

题目背景

重阳节到了,我们最好的八重樱拥有全村最好的嘤嘤刀……

题目描述

在绯玉丸力量的影响下,八重村成了一条长度为 nnn 的八重街,并且绯玉丸可以带着八重樱出现在街上的任意地点。而我们的八重樱则会在街上任意穿梭来获取某一地点上的嘤嘤嘤能量,用以升级她的嘤嘤刀。

在每个时刻,都会发生以下 333 个事件:

111 xxx valvalval 表示在 xxx 地点出现了携带着 valvalval 点嘤嘤嘤能量的绯狱丸,并且绯狱丸会吞噬该点的嘤嘤嘤能量,使得该点的嘤嘤嘤能量变为 val−ai val - a_ival−ai? 点,aia_iai? 为出现绯狱丸的前一刻,该点所存在的嘤嘤嘤能量。

222 lll rrr 表示绯玉丸会带着八重樱出现在[ lll , rrr ]间的任意一点。八重樱为了尽快升级她的嘤嘤刀,会获取该区间上最大的嘤嘤嘤能量。特殊的,为了保卫八重村,当 lll , rrr 之间存在绯狱丸时,八重樱会优先用她的嘤嘤刀对付绯狱丸,并获得绯狱丸此时拥有的 aia_iai? 点嘤嘤嘤能量。

333 lll rrr valvalval 绯玉丸会嘤嘤嘤,使得[ lll , rrr ]上的每一个地点的嘤嘤嘤能量增加 valvalval 点(包括绯狱丸)。

输入输出格式

输入格式:

第一行为 222 个数 nnn , mmm。

第二行为 nnn 个数,分别表示八重街上每个地点的初始嘤嘤嘤能量。

接下来 mmm 行,每行会发生 333 个事件中的一个,输入格式为题目描述中的格式。

输出格式:

对于每一个事件 222 ,你应当输出八重樱在该事件中获取的嘤嘤嘤能量并换行。

当所有事件结束时,如果嘤嘤刀积累的能量小于 100001000010000 ,你应当输出 QAQQAQQAQ 。

如果在[ 100001000010000 , 100000001000000010000000 )间,你应当输出 SakuraSakuraSakura。

如果都不符合,请输出 iceiceice。

输入输出样例

输入样例#1:
复制

10 10
1 2 3 4 5 6 7 8 9 10
2 1 10
2 1 10
2 1 10
2 1 10
2 1 10
2 1 10
2 1 10
2 1 10
2 1 10
2 1 10

输出样例#1: 复制

10
9
8
7
6
5
4
3
2
1
QAQ

输入样例#2: 复制

10 11
0 0 0 0 0 0 0 0 0 0
3 1 10 1
3 2 10 1
3 3 10 1
3 4 10 1
3 5 10 1
3 6 10 1
3 7 10 1
3 8 10 1
3 9 10 1
3 10 10 1
2 1 10

输出样例#2: 复制

10
QAQ

输入样例#3: 复制

10 13
0 0 0 0 0 0 0 0 0 0
1 10 10000
1 9 9000
1 8 8000
1 7 7000
1 6 6000
1 5 5000
1 4 4000
1 3 3000
1 2 2000
1 1 1000
2 10 10
2 8 8
2 8 10

输出样例#3: 复制

10000
8000
9000
Sakura

说明

对于所有的数据:

最终答案都会在 [0,231−1][0,2^{31}-1][0,231−1] 范围内;

nnn , mmm ?\leqslant? 100000100000100000。

值得注意的是,无论八重樱是获取了某一地点的嘤嘤嘤能量还是击败了某一地点的绯狱丸,该地点的嘤嘤嘤值都应当清零而不是保留原来的数值。

对于事件 222 ,题目保证每个事件中最多出现 111 只绯狱丸。

解题思路:

  看过题目之后就应该知道这题肯定与线段树有关。

  这题的难点应该是事件二,如何维护区间内是否有绯狱丸以及绯狱丸的位置,还有如何快速实现删除操作。

  对于事件1,:只是一个单点修改的操作,线段树维护就行了。

  对于事件3:只是一个区间加法的操作,线段树维护lazy标记,记得下传就行了。

  对于事件2:先考虑[x,y]区间内没有绯狱丸的情况,只需要查询区间最大值及其位置,至于删除无非就是将最大值删除,可以转换为将最大值所在位置清零,用单点修改便可实现。

  重点在于[x,y]区间有绯狱丸的情况,仔细阅读题目说明可以知道最多出现1只绯狱丸,由于我们是需要执行删除操作的,所以必须知道该绯狱丸所在的位置,这时我们就可以用树状数组来实现了,考虑用树状数组,在x位置加上值x,查询的时候至于要知道[x,y]区间的值是否为0即可,如果不为0,那么绯狱丸所在的位置便是查询的结果,再用线段树实现单点修改就行了,可谓妙啊!

   

  由于这题代码量较大,至今我的代码还没过 QWQ,所以只好贴标程了,自己的等A了之后再来填坑吧~~~

  思路是一致的,只是实现方面上略微不同。

标程:

  

#include <iostream>
#include <cstdio>
using namespace std;

int n,m;
int last_ans=0;
int p,x,y,v;
int se_tree[100000+100];
int add[100000*4+10];//因为重载了max,所以延迟标记要单独出来

struct node
{
    int maxx;
    int max_place;
    friend node max(node a,node b)
    {
        return a.maxx>b.maxx ? a:b;
    }//重载max
} tree[100000*4+10];

inline int read()
{
    int ans;
    char c;
    bool op=false;
    while(c=getchar(),c<‘0‘||c>‘9‘)
    {
        if(c==‘-‘) op=true;
    }
    ans=c-‘0‘;
    while(c=getchar(),c>=‘0‘&&c<=‘9‘)
    {
        ans=ans*10+c-‘0‘;
    }
    return op? -ans:ans;
}

void build(int l,int r,int num)
{
    if(l==r)
    {
        tree[num].maxx=read();
        tree[num].max_place=l;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,num<<1);
    build(mid+1,r,num<<1|1);
    tree[num]=max(tree[num<<1],tree[num<<1|1]);
}

inline void spread(int l,int r,int num)
{
    if(add[num])
    {
        int mid=(l+r)>>1;
        tree[num<<1].maxx+=add[num];
        tree[num<<1|1].maxx+=add[num];
        add[num<<1]+=add[num];
        add[num<<1|1]+=add[num];
        add[num]=0;
    }
}

void appear(int l,int r,int L,int val,int num)
{
    if(l==r)
    {
        tree[num].maxx=val-tree[num].maxx;
        return;
    }
    int mid=(l+r)>>1;
    spread(l,r,num);
    if(mid<L) appear(mid+1,r,L,val,num<<1|1);
    else appear(l,mid,L,val,num<<1);
    tree[num]=max(tree[num<<1],tree[num<<1|1]);
}

node ask(int l,int r,int L,int R,int num)
{
    if(l==L&&r==R)
    {
        return tree[num];
    }
    int mid=(l+r)>>1;
    spread(l,r,num);
    if(mid<L) return ask(mid+1,r,L,R,num<<1|1);
    else if(mid>=R) return ask(l,mid,L,R,num<<1);
    else return max(ask(l,mid,L,mid,num<<1),ask(mid+1,r,mid+1,R,num<<1|1));
}

void change(int l,int r,int L,int R,int val,int num)
{
    if(l==L&&r==R)
    {
        tree[num].maxx+=val;
        add[num]+=val;
        return;
    }
    int mid=(l+r)>>1;
    spread(l,r,num);
    if(mid<L) change(mid+1,r,L,R,val,num<<1|1);
    else if(mid>=R) change(l,mid,L,R,val,num<<1);
    else change(l,mid,L,mid,val,num<<1),change(mid+1,r,mid+1,R,val,num<<1|1);
    tree[num]=max(tree[num<<1],tree[num<<1|1]);
}

inline void se_add(int x,int val)
{
    for(; x<=n; x+=x&-x) se_tree[x]+=val;
}

inline int se_ask(int x)
{
    int ans=0;
    for(; x; x-=x&-x) ans+=se_tree[x];
    return ans;
}

int main()
{
    n=read();
    m=read();
    build(1,n,1);
    while(m--)
    {
        p=read();
        if(p==1)
        {
            x=read();
            v=read();
            appear(1,n,x,v,1);
            se_add(x,x);
        }
        else if(p==2)
        {
            x=read();
            y=read();
            int num=se_ask(y)-se_ask(x-1);
            if(num)
            {
                node ans=ask(1,n,num,num,1);
                printf("%d",ans.maxx);
                putchar(‘\n‘);
                last_ans+=ans.maxx;
                change(1,n,num,num,-ans.maxx,1);
                se_add(num,-num);
                continue;
            }
            node ans=ask(1,n,x,y,1);
            change(1,n,ans.max_place,ans.max_place,-ans.maxx,1);
            printf("%d",ans.maxx);
            putchar(‘\n‘);
            last_ans+=ans.maxx;
        }
        else
        {
            x=read();
            y=read();
            v=read();
            change(1,n,x,y,v,1);
        }
    }
    if(last_ans<10000) printf("QAQ");
    else  if(last_ans<10000000) printf("Sakura");
    else printf("ice");

    return 0;
}

原文地址:https://www.cnblogs.com/Dxy0310/p/9827535.html

时间: 2024-10-13 06:27:45

Luogu 45887 全村最好的嘤嘤刀(线段树 树状数组)的相关文章

线段树&amp;数状数组

线段树 单点修改,区间查询 #include<bits/stdc++.h> using namespace std; int n,q; long long num[1000010]; struct tree { int l,r; long long sum,max; }t[4000010]; void BuildTree(int,int,int); void Update(int,int,int,int,long long); long long Query(int,int,int,int,i

[luogu P3801] 红色的幻想乡 [线段树][树状数组]

题目背景 蕾米莉亚的红雾异变失败后,很不甘心. 题目描述 经过上次失败后,蕾米莉亚决定再次发动红雾异变,但为了防止被灵梦退治,她决定将红雾以奇怪的阵势释放. 我们将幻想乡看做是一个n*m的方格地区,一开始没有任何一个地区被红雾遮盖.蕾米莉亚每次站在某一个地区上,向东南西北四个方向各发出一条无限长的红雾,可以影响到整行/整列,但不会影响到她所站的那个地区.如果两阵红雾碰撞,则会因为密度过大而沉降消失.灵梦察觉到了这次异变,决定去解决它.但在解决之前,灵梦想要了解一片范围红雾的密度.可以简述为两种操

[模板]树状数组1/ZKW线段树

https://www.luogu.org/problemnew/show/P3374 1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 #define rson (o<<1|1) 5 #define lson (o<<1) 6 const int N = 530000<<1;//zkw线段树只能查询(0,bit),所以只有bit-2个叶节点,bit-2<n

树状数组 线段树

树状数组 树状数组的基本用途是维护序列的前缀和,相比前缀和数组,树状数组优势在于高效率的单点修改,单点增加(前缀和数组单点修改效率比较低) 因为树状数组的思想,原理还是很好理解的,就直接讲基本算法; 1 lowbit函数 关于lowbit这个函数,可能会有点难以理解,但其实你不理解也没关系,把模板背下来就好 根据任意正整数关于2的不重复次幂的唯一分解性质,例如十进制21用二进制表示为10101,其中等于1的位是第0,2,4(最右端是第0位)位,即21被二进制分解成\(2^4+2^2+2^0\);

[qbzt寒假]线段树和树状数组

树状数组 \(lowbit(x)=x\&(-x)\) 二维树状数组 修改某个点,查询(1,1)到(n,m)的前缀和(树状数组要从1开始) HDU2642 Stars \(YFF\)是个浪漫的人,他喜欢数天上的星星. 为了解决这个问题,我们考虑到天空是一个二维平面,有时星星会很亮,有时星星会很暗.首先,天空中没有明亮的星星,然后一些信息会被给出为"\(B\) \(x\) \(y\)",其中"\(B\)"表示明亮,\(x\)表示\(x\)坐标,\(y\)表示在\

Vijos P1066 弱弱的战壕【多解,线段树,暴力,树状数组】

弱弱的战壕 描述 永恒和mx正在玩一个即时战略游戏,名字嘛~~~~~~恕本人记性不好,忘了-_-b. mx在他的基地附近建立了n个战壕,每个战壕都是一个独立的作战单位,射程可以达到无限(“mx不赢定了?!?”永恒[email protected][email protected]). 但是,战壕有一个弱点,就是只能攻击它的左下方,说白了就是横纵坐标都不大于它的点(mx:“我的战壕为什么这么菜”ToT).这样,永恒就可以从别的地方进攻摧毁战壕,从而消灭mx的部队. 战壕都有一个保护范围,同它的攻击

Curious Robin Hood(树状数组+线段树)

1112 - Curious Robin Hood    PDF (English) Statistics Forum Time Limit: 1 second(s) Memory Limit: 64 MB Robin Hood likes to loot rich people since he helps the poor people with this money. Instead of keeping all the money together he does another tri

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制

【算法系列学习】线段树vs树状数组 单点修改,区间查询 [kuangbin带你飞]专题七 线段树 A - 敌兵布阵

https://vjudge.net/contest/66989#problem/A 单点修改,区间查询 方法一:线段树 http://www.cnblogs.com/kuangbin/archive/2011/08/15/2139834.html 1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<cmath> 6 #