题解 P6578【[Ynoi2019] 魔法少女网站】
题意
给你一个长为 序列的 ,支持操作共 次:
- 令 ;
- 查询 。
时间 3.5s,空间 64MB,。
思路
第十分块。
这是一篇序列分块题解!
写这个的原因是(听说)时间分块做法既难写又卡常。
我的码力真的不行,又写了三天/tuu
我们考虑一次查询,我们将所有 的数标为 ,其余标为 ,则查询的就是区间全 子段数量,对于一个长为 的极长连续全 段,它对答案的贡献为 。考虑到信息可并,我们对序列分块。
记 。
散块记录上一个 的位置 ,每扫到一个 的数或到达边界都要把答案加上 。
考虑整块需要 查询极长前后缀全 串长与块内答案,我们对一个块预处理以下信息:,当 的数为 时块内这三个信息的值,称其为块内第 个版本的信息。其中 为块长, 为这个块中所有数的排序后的数组。我们需要 将第 个版本的信息推到第 个版本的信息,维护数组 ,对于一个极长连续全 串 ,满足 。每次只会将一个 变为一个 ,则合并左右极长全 串并更新信息即可。此时,对于一个块预处理的复杂度是 的。
对于一次单点修改,我们 调整修改的块的排序数组,并 重算信息即可。
对于一次询问中的整块,我们只需查询 的非严格前驱的版本信息即可,则我们需要支持 求块内后继。具体地,我们维护值域分块,称一个数有值代表块中有这个数出现。令 表示 在其所在(值域)块中的后继, 表示(值域)第 块之后第一个有值的块, 表示(值域)块 中第一个有值的数。查询 的后继时,若 ,则后继为 ,否则为 。加入一个数 时把块内 ,相应更新 与 。删除同理。注意数可出现多次,不要进行无意义的删除/加入。
我们找到后继后,得出排名,取出所需的信息,进行块首块末合并即可。
块长要根据实现常数来调,我调了 。
时间复杂度 ,空间复杂度 。
注意到这里块间只需记录上个块的极长全 后缀,考虑离线逐块处理,空间复杂度降至 。
这题不卡常。
代码仅供参考:
#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~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探