zoukankan      html  css  js  c++  java
  • Luogu 45887 全村最好的嘤嘤刀(线段树 树状数组)

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

     

    题目背景

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

    题目描述

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

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

    111 xxx valvalval 表示在 xxx 地点出现了携带着 valvalval 点嘤嘤嘤能量的绯狱丸,并且绯狱丸会吞噬该点的嘤嘤嘤能量,使得该点的嘤嘤嘤能量变为 val−ai val - a_ivalai 点,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,2311] 范围内;

    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('
    ');
                    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('
    ');
                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;
    }
    View Code

     2018-10-22 11:09:32:

      程序AC啦,原来是个lxl题,题目竟然没有Special Judge???!!!,如果区间有多个最大值得话,删除哪一个并不确定,我的程序默认删除左儿子但是std默认删除右儿子。。。
    天大的坑,毁我青春。。。非常感谢
    https://www.luogu.org/space/show?uid=29215   帮我成功AC!!!。
     
    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define ll long long
    #define INF 0x3f3f3f3f
    #define maxn 100009
    #define maxm 
    inline ll read()
    {
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ll)(ch-'0');ch=getchar();}
        return x*f;
    }
    #define ls(x) x<<1
    #define rs(x) x<<1|1
    int val[maxn],mx[maxn<<2],add[maxn<<2],pos[maxn<<2],c[maxn];
    int n,m,k,tot,cnt,mxp,tot_ans=0;
    void insert(int x,int y)
    {
        while(x<=n)
        {
            c[x]+=y;
            x+=(x&(-x));
        }
    }
    
    int Query_have(int x)
    {
        int sum=0;
        while(x)
        {
            sum+=c[x];
            x-=(x&(-x));
        }
        return sum;
    }
    
    void push_up(int x)
    {
        mx[x]=mx[rs(x)],pos[x]=pos[rs(x)];
        if(mx[x]<mx[ls(x)])
            mx[x]=mx[ls(x)],pos[x]=pos[ls(x)];
    }
    
    void push_down(int x)
    {
        add[ls(x)]+=add[x];
        mx[ls(x)]+=add[x];
        add[rs(x)]+=add[x];
        mx[rs(x)]+=add[x];
        add[x]=0;
    }
    
    void built(int x,int l,int r)
    {
        if(l==r)
        {
            mx[x]=val[l];
            pos[x]=l;
            return ;
        }
        int mid=(l+r)>>1;
        built(ls(x),l,mid);
        built(rs(x),mid+1,r);
        push_up(x);
    }
    
    void Add(int x,int l,int r,int nl,int nr,int k)
    {
        if(nl<=l&&r<=nr)
        {
            add[x]+=k;
            mx[x]+=k;
            return ;
        }
        push_down(x);
        int mid=(l+r)>>1;
        if(nl<=mid)
            Add(ls(x),l,mid,nl,nr,k);
        if(mid<nr)
            Add(rs(x),mid+1,r,nl,nr,k);
        push_up(x);
    }
    
    void Update(int x,int l,int r,int p,int k,int f)
    {
        if(l==r)
        {
            if(!f)
                mx[x]=k-mx[x];
            else
                mx[x]=0;
            return ;
        }    
        push_down(x);
        int mid=(l+r)>>1;
        if(p<=mid)
            Update(ls(x),l,mid,p,k,f);
        else
            Update(rs(x),mid+1,r,p,k,f);
        push_up(x);
    }
    
    pair<int,int> Query(int x,int l,int r,int nl,int nr)
    {
        pair <int,int> ans;ans.first=-INF;
        if(nl<=l&&r<=nr)
            return make_pair(mx[x],pos[x]);
        push_down(x);
        int mid=(l+r)>>1;    
        if(mid<nr) 
            ans=Query(rs(x),mid+1,r,nl,nr);
        if(nl<=mid)
        {
            pair <int,int> temp=Query(ls(x),l,mid,nl,nr);
            if(temp.first>ans.first)
                ans=temp;
        }
        return ans;
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
    //    freopen("1.out","w",stdout);
        n=read(),m=read();
        for(int i=1;i<=n;i++)
            val[i]=read();
        built(1,1,n);
        for(int i=1;i<=m;i++)
        {
            int opt=read(),x=read(),y=read(),z;
            if(opt==1)
                Update(1,1,n,x,y,0),insert(x,x);
            else if(opt==2)
            {
                int id=Query_have(y)-Query_have(x-1);
                if(id)
                {
                    pair <int,int> temp=Query(1,1,n,id,id);
                    printf("%d
    ",temp.first);
                    tot_ans+=temp.first;
                    Update(1,1,n,temp.second,0,1);
                    insert(id,-id);
                    continue;
                }
                pair<int,int>  temp=Query(1,1,n,x,y);
                printf("%d
    ",temp.first);
                tot_ans+=temp.first;
                Update(1,1,n,temp.second,0,1);
            }
            else 
            {
                z=read();
                Add(1,1,n,x,y,z);
            }
        }
        if(tot_ans<10000)
            puts("QAQ");
        else if(tot_ans<10000000)
            puts("Sakura");
        else
            puts("ice");
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    View Code

     

  • 相关阅读:
    在返回值拒绝——reference
    传参时,使用引用替换变量
    C++设计与声明——让接口容易被正确使用
    三角函数的天下
    渲染流水线
    设置Shader关键字高亮(网上转)
    平衡二叉树,AVL树之代码篇
    2017-2018-1 20155205 嵌入式C语言——时钟
    GDB基础学习
    20155205 《信息安全系统设计基础》课程总结
  • 原文地址:https://www.cnblogs.com/Dxy0310/p/9827535.html
Copyright © 2011-2022 走看看