1. 程式人生 > >Luogu 45887 全村最好的嚶嚶刀(線段樹 樹狀數組)

Luogu 45887 全村最好的嚶嚶刀(線段樹 樹狀數組)

continue 應該 -- 記得 包括 code font 區間 clas

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

題目背景

重陽節到了,我們最好的八重櫻擁有全村最好的嚶嚶刀……

題目描述

在緋玉丸力量的影響下,八重村成了一條長度為 nnn 的八重街,並且緋玉丸可以帶著八重櫻出現在街上的任意地點。而我們的八重櫻則會在街上任意穿梭來獲取某一地點上的嚶嚶嚶能量,用以升級她的嚶嚶刀。

在每個時刻,都會發生以下 333 個事件:

111 xxx valvalval 表示在 xxx 地點出現了攜帶著 valvalval 點嚶嚶嚶能量的緋獄丸,並且緋獄丸會吞噬該點的嚶嚶嚶能量,使得該點的嚶嚶嚶能量變為 val−ai val - a_i

valai? 點,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(\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;
}
View Code

Luogu 45887 全村最好的嚶嚶刀(線段樹 樹狀數組)