题解 P6578【[Ynoi2019] 魔法少女网站】

$\text{Link}$

题意

给你一个长为 $n$ 序列的 $a$,支持操作共 $q$ 次:

  1. 令 $a_x\gets y$;
  2. 查询 $\displaystyle\sum_{i=l}^r\sum_{j=i}^r[\max_{k=i}^ja_k\le x]$。

时间 3.5s,空间 64MB,$1\le a_i\le n\le 3\times 10^5$。

思路

第十分块。

这是一篇序列分块题解!

写这个的原因是(听说)时间分块做法既难写又卡常。

我的码力真的不行,又写了三天/tuu


我们考虑一次查询,我们将所有 $\le x$ 的数标为 $1$,其余标为 $0$,则查询的就是区间全 $1$ 子段数量,对于一个长为 $k$ 的极长连续全 $1$ 段,它对答案的贡献为 $\dfrac{k(k+1)}2$。考虑到信息可并,我们对序列分块。

记 $f(l,r)=\dfrac{(r-l+1)(r-l+2)}{2}$。

散块记录上一个 $>x$ 的位置 $las$,每扫到一个 $>x$ 的数或到达边界都要把答案加上 $f(las+1,i-1)$。

考虑整块需要 $O(1)$ 查询极长前后缀全 $1$ 串长与块内答案,我们对一个块预处理以下信息:$\forall i\in[1,len]$,当 $\le b_i$ 的数为 $1$ 时块内这三个信息的值,称其为块内第 $i$ 个版本的信息。其中 $len$ 为块长,$b_i$ 为这个块中所有数的排序后的数组。我们需要 $O(1)$ 将第 $i$ 个版本的信息推到第 $i+1$ 个版本的信息,维护数组 $lis$,对于一个极长连续全 $1$ 串 $[l,r]$,满足 $lis_l=r,lis_r=l$。每次只会将一个 $0$ 变为一个 $1$,则合并左右极长全 $1$ 串并更新信息即可。此时,对于一个块预处理的复杂度是 $O(\sqrt n)$ 的。

对于一次单点修改,我们 $O(\sqrt n)$ 调整修改的块的排序数组,并 $O(\sqrt n)$ 重算信息即可。

对于一次询问中的整块,我们只需查询 $x$ 的非严格前驱的版本信息即可,则我们需要支持 $O(\sqrt n)-O(1)$ 求块内后继。具体地,我们维护值域分块,称一个数有值代表块中有这个数出现。令 $su_{2,x}$ 表示 $x$ 在其所在(值域)块中的后继,$su_{1,t}$ 表示(值域)第 $t$ 块之后第一个有值的块,$fir_t$ 表示(值域)块 $t$ 中第一个有值的数。查询 $x$ 的后继时,若 $su_{2,x}\ne 0$,则后继为 $su_{2,x}$,否则为 $fir_{su_{1,bel_x}}$。加入一个数 $x$ 时把块内 $\forall a<x,su_{2,a}=su_{2,x},su_{2,a}\gets x$,相应更新 $fir_{bel_x}$ 与 $su_{1,t}$。删除同理。注意数可出现多次,不要进行无意义的删除/加入。

我们找到后继后,得出排名,取出所需的信息,进行块首块末合并即可。

块长要根据实现常数来调,我调了 $650$。

时间复杂度 $O((n+q)\sqrt n)$,空间复杂度 $O(n\sqrt n)$

注意到这里块间只需记录上个块的极长全 $1$ 后缀,考虑离线逐块处理,空间复杂度降至 $O(n)$。


这题不卡常。

代码仅供参考:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=3e5+10,sq=650,nq=N/sq+10;
int n,q,blk,a[N],bl[nq],br[nq],bel[N],len[nq];
int pre[nq][sq+10],suf[nq][sq+10];
ll ans[nq][sq+10],res[N],wrk[N];
struct Tem{
    int x,id;
    inline friend bool operator<(const Tem &a,const Tem &b){
        return a.x==b.x?a.id<b.id:a.x<b.x;
    }
};
struct Operate{
    int opt,l,r,x;
}que[N];
vector<Tem>b[nq];
int lis[N];
inline ll count(int l,int r){
    return l<=r?wrk[r-l+1]:0;
}
int vis[N],v2[N];
inline void init(int bL,int bR){
    int d=bel[bL];
    int len=b[d].size();
    for(int i=bL;i<=bR;i++)
        lis[i]=0,vis[i]=0;
    lis[bL-1]=lis[bR+1]=vis[bL-1]=vis[bR+1]=0;
    for(int i=0;i<len;i++){
        Tem tmp=b[d][i];
        int id=tmp.id;
        pre[d][i+1]=pre[d][i],suf[d][i+1]=suf[d][i];
        if(!lis[id-1]&&!lis[id+1]){
            lis[id]=id,ans[d][i+1]=ans[d][i]+1;
            if(id==bL) pre[d][i+1]=1;
            if(id==bR) suf[d][i+1]=1; 
        }else if(!lis[id+1]){
            int pr=lis[id-1];
            lis[pr]=id,lis[id]=pr;
            if(pr!=id-1) lis[id-1]=0;
            ans[d][i+1]=ans[d][i]-count(pr,id-1)+count(pr,id);
            if(pr==bL) pre[d][i+1]=pre[d][i]+1;
            if(id==bR) suf[d][i+1]=bR-pr+1;
        }else if(!lis[id-1]){
            int sf=lis[id+1];
            lis[id]=sf,lis[sf]=id;
            if(sf!=id+1) lis[id+1]=0;
            ans[d][i+1]=ans[d][i]-count(id+1,sf)+count(id,sf);
            if(id==bL) pre[d][i+1]=sf-bL+1;
            if(sf==bR) suf[d][i+1]=suf[d][i]+1;
        }else{
            int pr=lis[id-1],sf=lis[id+1];
            lis[pr]=sf,lis[sf]=pr;
            if(pr!=id-1) lis[id-1]=0;
            if(sf!=id+1) lis[id+1]=0;
            ans[d][i+1]=ans[d][i]-count(pr,id-1)-count(id+1,sf)+count(pr,sf);
            if(pr==bL) pre[d][i+1]=sf-bL+1;
            if(sf==bR) suf[d][i+1]=bR-pr+1;
        }
    }
}
int su1[N],su2[N],fir[N],rnk[N],knr[N];
int val[N],sum[N];
int PRE[N];
inline void solve(int bL,int bR){
    int d=bel[bL];
    init(bL,bR);
    memset(val,0,sizeof(val));
    memset(sum,0,sizeof(sum));
    for(int i=bL;i<=bR;i++)
        val[a[i]]++,sum[bel[a[i]]]++;
    for(int i=n;i>=1;i--)
        if(val[i]) fir[bel[i]]=i;
    for(int i=blk,j=0;i>=1;i--){
        su1[i]=j;
        if(sum[i]) j=i;
        for(int k=br[i],l=0;k>=bl[i];k--){
            su2[k]=l;
            if(val[k]) l=k;
        }
    }
    for(int i=1;i<=n;i++)
        rnk[i]=0;
    for(int i=0;i<b[d].size();i++){
        knr[i+1]=b[d][i].x;
        if(!rnk[b[d][i].x]) rnk[b[d][i].x]=i+1;
    }
    for(int id=1;id<=q;id++){
        int opt=que[id].opt,l=que[id].l,r=que[id].r,x=que[id].x;
        if(opt==1){
            if(l<bL||l>bR) continue;
            int d=bel[l];
            for(int i=0;i<b[d].size();i++)
                rnk[b[d][i].x]=0;
            b[d].erase(lower_bound(b[d].begin(),b[d].end(),(Tem){a[l],l}));
            int t=a[l];
            a[l]=r;
            b[d].insert(lower_bound(b[d].begin(),b[d].end(),(Tem){a[l],l}),{a[l],l});
            for(int i=0;i<b[d].size();i++){
                knr[i+1]=b[d][i].x;
                if(!rnk[b[d][i].x]) rnk[b[d][i].x]=i+1;
            }
            init(bL,bR);
            int bL=bel[t];
            if(val[t]==1){
                for(int i=bl[bL];i<=br[bL];i++)
                    if(su2[i]==t) su2[i]=su2[t];
                if(sum[bL]==1)
                    for(int i=1;i<=bL;i++)
                        if(su1[i]==bL) su1[i]=su1[bL];
            }
            val[t]--,sum[bL]--;
            bool tmpp=0;
            for(int i=bl[bL];i<=br[bL];i++)
                if(val[i]){ fir[bL]=i;tmpp=1;break; }
            if(!tmpp) fir[bL]=0;
            t=a[l],bL=bel[t];
            if(!val[t]){
                int tmp=knr[rnk[t]+1];
                if(tmp>br[bL]) tmp=0;
                for(int i=bl[bL];i<t;i++)
                    if(su2[i]==tmp)
                        su2[i]=t;
                if(!sum[bL])
                    for(int i=1;i<bL;i++)
                        if(su1[i]==su1[bL]) su1[i]=bL;
            }
            val[t]++,sum[bL]++;
            tmpp=0;
            for(int i=bl[bL];i<=br[bL];i++)
                if(val[i]){ fir[bL]=i;tmpp=1;break; }
            if(!tmpp) fir[bL]=0;
        }else{
            if(r<bL||l>bR) continue;
            int L=bel[l],R=bel[r];
            if(L==R){
                int i,las;
                for(i=l,las=l-1;i<=r;i++)
                    if(a[i]>x)
                        res[id]+=count(las+1,i-1),las=i;
                res[id]+=count(las+1,i-1);
                continue;
            }
            if(d==L){
                int i=l,las=l-1;
                for(;i<=bR;i++)
                    if(a[i]>x)
                        res[id]+=count(las+1,i-1),las=i;
                res[id]+=count(las+1,i-1);
                for(int i=bR;i>=l;i--){
                    if(a[i]<=x) PRE[id]=i;
                    else break;
                }
                if(!PRE[id]) PRE[id]=bR+1;
            }else if(d==R){
                int las=PRE[id],i=bL;
                for(;a[i]<=x&&i<=r;i++);
                res[id]=res[id]-count(PRE[id],bL-1)+count(PRE[id],i-1);
                for(las=i-1;i<=r;i++)
                    if(a[i]>x)
                        res[id]+=count(las+1,i-1),las=i;
                res[id]+=count(las+1,i-1);
            }else{
                int tmp=0;
                if(su2[x]) tmp=su2[x];
                else if(su1[bel[x]]) tmp=fir[su1[bel[x]]];
                int p;
                if(tmp) p=rnk[tmp]-1;
                else p=len[d];
                int t=PRE[id]; 
                res[id]=res[id]-count(t,bL-1)-count(bL,bL+pre[d][p]-1)+count(t,bL+pre[d][p]-1)+ans[d][p];
                if(suf[d][p]!=len[d]) PRE[id]=bR-suf[d][p]+1;
            }
        }
    }
}
int main(){
//  freopen("make_data.txt","r",stdin);
//  freopen("your.txt","w",stdout);
    n=read(),q=read();
    for(int i=1;i<=n;i++)
        a[i]=read(),wrk[i]=1ll*i*(i+1)/2;
    for(int i=1;i<=n;i+=sq){
        blk++;
        bl[blk]=i,br[blk]=min(i+sq-1,n);
        len[blk]=br[blk]-bl[blk]+1;
        for(int j=bl[blk];j<=br[blk];j++)
            bel[j]=blk,b[blk].push_back({a[j],j}),lis[j]=0;
        sort(b[blk].begin(),b[blk].end());
    }
    for(int i=1;i<=q;i++){
        int opt=read(),l=read(),r=read();
        que[i]={opt,l,r,opt==1?0:read()};
    }
    for(int i=1;i<=blk;i++)
        solve(bl[i],br[i]);
    for(int i=1;i<=q;i++)
        if(que[i].opt==2)
            write(res[i]),putc('\n');
    flush();
}

再见 qwq~

posted @ 2022-03-24 06:51  ffffyc  阅读(63)  评论(0)    收藏  举报  来源