TAMAYA

彬彬冰激凌發表於2024-10-16

TAMAYA

挺有意思的維護題。

題面

n個小夫坐成一排,每個小夫有一個真實值vi。小夫們有m場聚會,第i次聚會會在編號為 [li, ri] 的小夫中舉辦。

聚會之後,這些小夫的真實值會變為他們之中的真實值的最大值。將會發生q次事件,有兩類事件。

  • 第一類事件,第x個小夫的真實值變成了y。
  • 第二類事件,小夫們向你提問,假如第L次聚會到第R次聚會按順序舉辦,第x個 小夫的真實值會變成多少。

對於所有測試點:\(n,m,q\leq 5\times 10^5,l_i\leq r_i\leq n,x\leq n,L\leq R,op\in\{1,2\},v_i,y\leq 10^9\)

思路

下文稱聚會為操作,提問為查詢。

只考慮 \(op=2\) 的情況,對於一個詢問中的 \(x_i\),令 \([lx,rx]=[x_i,x_i]\)

倒序進行詢問中的操作 \([L_i,R_i]\),若一次操作 \([l_j,r_j](j\in [L_i,R_i])\) 與現在的 \([lx,rx]\) 有交集,那麼令 \(lx=\min(lx,l_j)\)\(rx=\max(rx,r_j)\);形式化的,若 \([l_j,r_j]\cap[lx,rx]\neq \emptyset\),那麼令 \([lx,rx]=[l_j,r_j]\cup[lx,rx]\)

最後的答案就是 \([lx,rx]\) 區間中的最大值。

考慮到每一個查詢的 \([x_i,x_i]\),在第一次取並集後,會以一個操作中的區間 \([l_j,r_j]\) 的形式出現。考慮對於一個操作 \(j\),維護出執行了倒敘執行操作 \(k\sim j(k<j)\) 區間左端點和右端點的變化。

但是這樣的複雜度來到了 \(O(m^2)\)

考慮倍增維護,維護出合併 \(2^k\) 次操作後的區間的左右端點的變化。

由於 \(l_j\) 合併只會變成前面某一次操作的 \(l\),這樣就可以倍增維護出變成了哪一次操作的 \(l\)

\(r\) 同理。

最後對於查詢 \(x_i\),維護出最後一次被操作覆蓋即可。

CODE

#include<bits/stdc++.h>
using namespace std;

const int maxn=5e5+5;

int n,m,q;
int v[maxn],pl[maxn][25],pr[maxn][25],l[maxn],r[maxn],L[maxn],R[maxn],id[maxn],op[maxn],x[maxn];

vector<int>vec[maxn];

namespace linetree
{
    #define lch(p) p*2
    #define rch(p) p*2+1
    struct treenode{int mx,lazy;}tr[maxn*20];
    inline void updata(int p) {tr[p].mx=max(tr[lch(p)].mx,tr[rch(p)].mx);}
    inline void push_down(int p,int l,int r)
    {
        if(!tr[p].lazy) return ;
        if(l==r) return ;
        tr[lch(p)].mx=max(tr[p].lazy,tr[lch(p)].mx);
        tr[lch(p)].lazy=max(tr[p].lazy,tr[lch(p)].lazy);
        tr[rch(p)].mx=max(tr[p].lazy,tr[rch(p)].mx);
        tr[rch(p)].lazy=max(tr[p].lazy,tr[rch(p)].lazy);
        tr[p].lazy=0;
    }
    inline void change(int p,int l,int r,int lx,int rx,int v)
    {
        if(r<lx||l>rx) return ;
        push_down(p,l,r);
        if(lx<=l&&r<=rx)
        {
            tr[p].mx=v;tr[p].lazy=v;
            return ;
        }
        int mid=(l+r)>>1;
        change(lch(p),l,mid,lx,rx,v);change(rch(p),mid+1,r,lx,rx,v);
        updata(p);
    }
    inline int qry(int p,int l,int r,int lx,int rx)
    {
        if(r<lx||l>rx) return 0;
        push_down(p,l,r);
        if(lx<=l&&r<=rx) return tr[p].mx;
        int mid=(l+r)>>1;
        return max(qry(lch(p),l,mid,lx,rx),qry(rch(p),mid+1,r,lx,rx));
    }
    inline void clr(){memset(tr,0,sizeof(tr));}
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&v[i]);
    for(int i=1;i<=m;i++) scanf("%d%d",&l[i],&r[i]);
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d%d",&op[i],&L[i],&R[i]);
        if(op[i]==2) scanf("%d",&x[i]),vec[R[i]].emplace_back(i);
    }
    for(int i=1;i<=m;i++)
    {
        pl[i][0]=linetree::qry(1,1,n,l[i],l[i]);
        pr[i][0]=linetree::qry(1,1,n,r[i],r[i]);
        linetree::change(1,1,n,l[i],r[i],i);
        for(auto v:vec[i]) id[v]=linetree::qry(1,1,n,x[v],x[v]);
    }
    for(int i=1;i<=m;i++) for(int j=1;j<=log2(m);j++)
    {
        pl[i][j]=pl[pl[i][j-1]][j-1];
        pr[i][j]=pr[pr[i][j-1]][j-1];
    }
    linetree::clr();
    for(int i=1;i<=n;i++) linetree::change(1,1,n,i,i,v[i]);
    for(int i=1;i<=q;i++)
    {
        if(op[i]==1)
            linetree::change(1,1,n,L[i],L[i],R[i]);
        else
        {
            if(id[i]<L[i]) {printf("%d\n",linetree::qry(1,1,n,x[i],x[i]));continue;}
            int lx=id[i],rx=id[i];
            for(int j=log2(m);~j;j--) if(pl[lx][j]>=L[i]) lx=pl[lx][j];
            for(int j=log2(m);~j;j--) if(pr[rx][j]>=L[i]) rx=pr[rx][j];
            printf("%d\n",linetree::qry(1,1,n,l[lx],r[rx]));
        }
    }
}