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

Link

题意

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

  1. axy
  2. 查询 i=lrj=ir[maxk=ijakx]

时间 3.5s,空间 64MB1ain3×105

思路

第十分块。

这是一篇序列分块题解!

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

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


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

f(l,r)=(rl+1)(rl+2)2

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

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

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

对于一次询问中的整块,我们只需查询 x 的非严格前驱的版本信息即可,则我们需要支持 O(n)O(1) 求块内后继。具体地,我们维护值域分块,称一个数有值代表块中有这个数出现。令 su2,x 表示 x 在其所在(值域)块中的后继,su1,t 表示(值域)第 t 块之后第一个有值的块,firt 表示(值域)块 t 中第一个有值的数。查询 x 的后继时,若 su2,x0,则后继为 su2,x,否则为 firsu1,belx。加入一个数 x 时把块内 a<x,su2,a=su2,x,su2,ax,相应更新 firbelxsu1,t。删除同理。注意数可出现多次,不要进行无意义的删除/加入。

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

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

时间复杂度 O((n+q)n)空间复杂度 O(nn)

注意到这里块间只需记录上个块的极长全 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 @   ffffyc  阅读(47)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
点击右上角即可分享
微信分享提示