[题目记录]P8415/P10151 SMV CC-64“蝰蛇”

news/2025/1/10 17:11:31/文章来源:https://www.cnblogs.com/youlv/p/18664311

P8415/P10151 SMV CC-64“蝰蛇”

题意

给序列 \(a_1,\dots,a_n\) 和排列 \(b_1,\dots,b_n\),共有 \(m\) 次操作:

修改操作:给定 \(x,y\),将 \(a_x\) 改为 \(y\)

查询操作:给定 \(l,r,x\),查区间 \([l,r]\) 内最长的子区间 \([l',r']\)(即满足 \(l\le l'\le r'\le r\)),使得对 \(l'\le i<r'\)\(a_{i+1}=b_{a_i}\),且存在 \(l'\le i\le r'\) 使得 \(a_i=x\)。需要输出满足条件的 \(r'-l'+1\) 的最大值,若不存在则输出 \(0\)

\(n,m\le 10^6 , 1\le a_i,b_i \le n\)

题解

显然 , 查询操作的限制是两部分 . 对于前一部分 , 可以发现整个序列被划分成了若干个合法段 , 只有这些合法段和它们的子段可能作为答案 . 每次单点修改只会对这个段造成 \(O(1)\) 次修改 , 可以直接用 set 维护 .

对于后一部分 , 要求段内有值 \(x\) , 这个限制把问题拓展了一个值域维度 . 为了简化问题可以先单独处理区间两端不完整的合法段 , 其余在中间的完整段必然整段贡献 .

这样 , 这些完整段的贡献可以挂在端点上 , 于是问题变成了在序列上若干个位置 , 每个位置覆盖了值域上的若干个点 , 求序列区间中包含了 \(x\) 的所有位置权值最大值 , 这显然是个二维问题 , 直接分成序列 - 值域两个维度考虑 , 发现这相当于在平行于值域轴的直线上修改 , 在平行于序列轴的一条线段上查询最大值 .

现在的修改不够美观 , 发现一个合法段内部元素肯定是一个置换环上的连续一段 , 因此把值域维度映射到把所有置换环展开后前后连接 , 这样每一个合法段的值域在值域上只有 \(O(1)\) 个区间 .

用线段树套平衡树维护二维平面 . 线段树维护值域维 , 内部平衡树维护序列 . 运用标记永久化的技巧 , 在值域区间上的 $O(\log n) $ 个节点上 , 插入或删除序列一个位置的权值 . 查询时 , 在线段树从根到 \(x\) 对应叶子的链上全都查询一遍 .

时间复杂度 \(O(n\log^2 n)\) ,空间复杂度 $O(n \log n) $ .

细节不少 :

  • set 维护每一个极长合法段的端点 , 每次单点修改时要分别考虑左右 , 从联通到不连通或者从不连通到联通 .
  • 注意空间常数 , 因为这道题里插入的线段是会删除的 , 最坏情况下 , 每次修改要进行 3 次删除 , 因此空间最坏情况下带一个 \(4\) 倍常数 , 当然也可以写空间回收 .
  • 查询不完整段 , 可以用 set 维护值为 \(x\) 的所有下标 , 这样就可以容易地判定不完整段是否合法 .
点击查看代码
#include<bits/stdc++.h>
#define file(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define ll long long
#define INF 0x7fffffff
#define INF64 1e18
using namespace std;constexpr int N=1e6+5;int n,m;struct node{int ls,rs,rnd;pair<int,int> val;int mx;
}a[N*80];inline void pushup(int x){a[x].mx=max(a[a[x].ls].mx,max(a[a[x].rs].mx,a[x].val.second));
}int at;int add(int pos,int val){a[++at]={0,0,rand(),{pos,val},val};return at;
}int merge(int x,int y){if(x==0||y==0) return x+y;if(a[x].rnd<=a[y].rnd){a[x].rs=merge(a[x].rs,y);pushup(x);return x;}a[y].ls=merge(x,a[y].ls);pushup(y);return y;
}void split(int u,pair<int,int> k,int &x,int &y){if(!u){ x=y=0;return;}if(a[u].val<=k){x=u;split(a[u].rs,k,a[x].rs,y);pushup(x);}else{y=u;split(a[u].ls,k,x,a[y].ls);pushup(y);}
}struct tnode{int ls,rs,rt;
}t[N*4];void build(int x,int l,int r){t[x].rt=0;if(l==r) return;t[x].ls=x*2;t[x].rs=x*2+1;int mid=l+r>>1;build(t[x].ls,l,mid);build(t[x].rs,mid+1,r);
}void update(int x,int L,int R,int l,int r,int pos,int val){if(l<=L&&R<=r){int p=0,q=0;split(t[x].rt,{pos,val},p,q);t[x].rt=merge(p,merge(add(pos,val),q));}else if(!(r<L||R<l)){int mid=L+R>>1;update(t[x].ls,L,mid,l,r,pos,val);update(t[x].rs,mid+1,R,l,r,pos,val);}
}int query(int x,int L,int R,int pos,int lp,int rp){int p=0,q=0,u=0,v=0;split(t[x].rt,{lp-1,n+1},p,q);split(q,{rp,n+1},u,v);int res=a[u].mx;t[x].rt=merge(p,merge(u,v));if(L==R) return res;int mid=L+R>>1;if(pos<=mid) res=max(res,query(t[x].ls,L,mid,pos,lp,rp));else res=max(res,query(t[x].rs,mid+1,R,pos,lp,rp) );return res;
}void remove(int x,int L,int R,int l,int r,int pos,int val){if(l<=L&&R<=r){int p=0,q=0,u=0,v=0;split(t[x].rt,{pos,val-1},p,q);split(q,{pos,val},u,v);t[x].rt=merge(p,v);}else if(!(r<L||R<l)){int mid=L+R>>1;remove(t[x].ls,L,mid,l,r,pos,val);remove(t[x].rs,mid+1,R,l,r,pos,val);}
}int b[N],p[N];int dfn[N],rnk[N],tot,len[N],rnd[N],L[N],R[N];set<int> st;
set<int> pos[N];inline void solve(int l,int r,int x,int y){int ln=y-x+1;int tl=dfn[l],tr=dfn[r];if(ln>=len[rnd[l]]){update(1,1,n,L[rnd[l]],R[rnd[l]],x,ln);return;}if(tl+ln-1<=R[rnd[l]]) update(1,1,n,tl,tr,x,ln);else {update(1,1,n,tl,R[rnd[l]],x,ln);update(1,1,n,L[rnd[l]],tr,x,ln);}
}inline void delt(int l,int r,int x,int y){int ln=y-x+1;int tl=dfn[l],tr=dfn[r];if(ln>=len[rnd[l]]){remove(1,1,n,L[rnd[l]],R[rnd[l]],x,ln);return;}if(tl+ln-1<=R[rnd[l]]) remove(1,1,n,tl,tr,x,ln);else {remove(1,1,n,tl,R[rnd[l]],x,ln);remove(1,1,n,L[rnd[l]],tr,x,ln);}
}inline void modify(int x,int y){if(b[x]==y) return;auto it=st.lower_bound(x);int lp,rp;rp=*it;lp=*(--it);delt(b[lp+1],b[rp],lp+1,rp);if(x==rp){if(x!=n&&b[x+1]==p[y]){auto itt=st.upper_bound(rp);delt(b[rp+1],b[*itt],rp+1,*itt);st.erase(rp);rp=*itt;}}else{if(b[x+1]!=p[y]){solve(b[x+1],b[rp],x+1,rp);st.insert(x);rp=x;}}if(x==lp+1){if(x!=1&&y==p[b[x-1]]){auto itt=prev(st.lower_bound(lp));delt(b[(*itt)+1],b[lp],(*itt)+1,lp);st.erase(lp);lp=*itt;}}else{if(y!=p[b[x-1]]){solve(b[lp+1],b[x-1],lp+1,x-1);st.insert(x-1);lp=x-1;}}pos[b[x]].erase(x);pos[y].insert(x);b[x]=y;solve(b[lp+1],b[rp],lp+1,rp);
}inline int ask(int l,int r,int x){int lp=*st.lower_bound(l),rp=(*prev(st.lower_bound(r)))+1;if(lp>=r){auto it=pos[x].lower_bound(l);if(it==pos[x].end()||(*it>r)) return 0;return r-l+1;}int res=0;auto it=pos[x].lower_bound(l);if(it!=pos[x].end()&&(*it<=lp) ) res=max(res,lp-l+1);it=pos[x].lower_bound(rp);if(it!=pos[x].end()&&(*it<=r) ) res=max(res,r-rp+1);if(lp+1<=rp-1) res=max(res, query(1,1,n,dfn[x],lp+1,rp-1)) ;return res;
}int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin>>n>>m;for(int i=1;i<=n;i++) cin>>b[i];for(int i=1;i<=n;i++) cin>>p[i];for(int i=1;i<=n;i++) L[i]=n+1,R[i]=0;for(int i=1;i<=n;i++){int t=i;while(!dfn[t]){rnd[t]=i;len[i]++;dfn[t]=++tot; rnk[tot]=t;L[i]=min(L[i],tot);R[i]=max(R[i],tot);t=p[t];}}build(1,1,n);st.insert(0);for(int i=1;i<=n;i++){pos[b[i]].insert(i);if(i==n||b[i+1]!=p[b[i]]){int last;last=(*prev(st.end()));solve(b[last+1],b[i],last+1,i);st.insert(i);}}for(int i=1;i<=m;i++){int op,x,y,c;cin>>op>>x>>y;if(op==1){modify(x,y);}else{cin>>c;cout<<ask(x,y,c)<<'\n';}}
}

总结

lxl 把这道题扔到了模拟赛里 , 然而这道题只是 3 道题里第二难的 .

其实这道题和极度毒瘤的数据结构比还算简单很多 , 因为这道题的思路比较自然 , 基本全程都是对着问题解决问题 , 能简化的就简化 . 最智慧的一步是把值域散点 , 通过映射到置换环上 , 转化为区间 .要利用好题目的特性 , 转化和找性质不分家 , 要时刻落实到题目的性质上 .

但是实现难度还是有的 , 而且要时刻注意常数 , 尤其是空间常数 . 还是要求一定代码能力的 , 能结构化的一定结构化 , 时刻写常数最小的方法 , 要能耐得住性子讨论 .

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/867225.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

找不到 .NETFramework,Version=v4.0 的引用程序集问题

高版本操作系统默认安装.NETFramework4.6或以上时,系统用4.0的msbuild 命令编译导致 找不到4.0的程序集问题 1.下载nuget版本的资源包 https://www.nuget.org/packages/Microsoft.NETFramework.ReferenceAssemblies.net40/ microsoft.netframework.referenceassemblies.net40.…

嵌入式截屏工具-gsnap移植 arm平台

# 目录 - [前言](#前言) - [正文](#正文)- [原理](#原理)- [环境](#环境)- [详细流程](#详细流程)- [使用](#使用) - [参考](#参考)# 正文 ## 原理 ### 前提条件: 嵌入式机器要支持`/dev/fb0`这个设备节点。不然,这个工具基本就使用不了了。### 原理: 由于 Linux 系统的 Frame…

AIME2019 I

AIME2019 I 的题目订正。√√√√ √√.√√ ...√. \(= 9\) https://artofproblemsolving.com/wiki/index.php/2019_AIME_I_Problems P4 寄啦! 唐题。分讨 substitution 的次数即可。 P5 对啦! DP。 令 \(f_{x,y}\) 为走到 \((x,y)\) 的概率。边界情况为 \(f_{4,4} = 1\)。答…

通过本地私有的镜像仓库harbor解决网络原因导致的jdk无法加载而造成的docker打包错误.v2.250110

​各种网络原因,或是docker.io无法访问,或是阿里的镜像源故障,导致java打包发布的时候报错,很影响代码发布的质量。解决思路:墙出去把jdk下载下来,代码使用本地的harbor库进行引用,一劳永逸。此解决方法也适用于国外优质不频繁变动的镜像源的本地化使用。解决方法 1. 自…

[题目记录]AGC005E Sugigma: The Showdown

一道通过自己生成思路做出的思维题 . 通过分析博弈过程发现问题其实没有那么复杂 , 然后层层分析转化问题即可 .AGC005E Sugigma: The Showdown 题意 给出两棵树 , 点的编号相同 , 连边方式不同 . 初始 A 在树 \(a\) 上的点 \(x\) , B 在树 \(b\) 上的点 \(y\) , 两人轮流走 , …

代码随想论算法训练营第3天 | 链表理论基础,203.移除链表元素,707.设计链表,206.反转链表

一、刷题部分 1.1 链表理论基础原文链接:代码随想录 题目链接:🈚️链表是由一个个节点串联而成的,节点包含数据域和指针域,数据域用来存放数据,而指针域实现了节点之间的串联。 链表中有单链表、双链表、循环链表:链表的物理空间是不连续的,通过指针存储下一节点的物理…

ABAP配置:OY01 定义国家/地区

配置:OY01 定义国家/地区 事务代码:OY01 配置路径 SPRO-ABAP平台-常规设置-设置国家-定义国家/地区 配置路径截图配置描述 国家是SAP里面一个非常重要的概念,SAP国家概念涉及公司代码、工厂、主数据、跨国银行交易,系统默认自带ISO相关的国家编码,在S/4中,一些配置转移到…

关于GTM,这些评价指标你都知道吗?

目标跟踪指标是企业实现持续增长和盈利的重要工具。通过定期监控和分析这些指标,企业可以及时发现潜在问题并采取相应的改进措施,以保持其竞争力并实现业务目标。因此,企业应该重视这些指标的应用,并不断优化其监控和分析流程,以确保其业务运营的顺利进行。Goal Tracking …

ODX诊断数据库转换工具 - DDC

INTEWORK-DDC (Diagnostic Database Convertor) 是将诊断调查问卷转换为标准ODX(2.2.0)数据库的工具。ODX是格式标准化的诊断数据库文件,我们在诊断不同的车或者不同的ECU时,只需要加载适配这个车型或ECU的ODX文件即可,而无需对诊断仪做任何改变。ODX统一了诊断文件的格式,…

异地多活架构进阶:如何解决写后立即读场景问题?【转】

在《醍醐灌顶!异地多活架构设计看这篇就够了》一文中,基于容灾需要,讨论了数据写入的架构模型。数据读取方面,重点在于解决读取请求的负载分担、路由选择的问题,对于容灾架构的选择影响不大。不过,其中的“写后立即读”场景,是个一致性范畴的问题,即写入的数据和写入后…

JAVA之面向对象

1、设计对象并使用类和对象 类(设计图):是对象共同特征的描述; 对象:是真实存在的具体实例; 在java中,必须先设计类,才能获得对象。 如何得到类的对象:类名 对象名 = new 类名(); 如何使用对象: 访问属性:对象名.成员变量 访问行为:对象名.方法…

免费手动打Windows Server补丁

免费手动打Windows Server 2008 R2补丁https://catalog.update.microsoft.com/search.aspx?q=kb4474419然后到windows上双击运行即可本文来自博客园,作者:六月OvO,转载请注明原文链接:https://www.cnblogs.com/chenlifan/p/18664077