P7838 解题报告

题目:P7838 「Wdoi-3」夜雀 treating

题解

method 1 暴力

信仰 2n,谁试谁爽。

method 2 观察性质

首先取掉 an+1,然后就不管它了,现在还剩下 2n 个数。

在不取数的情况下,数列中点在第 n 个数和第 n+1 个数之间。每次在删除一个数后,中点会移动到第 n 个数或者第 n+1 个数。并且容易发现如果删除的数在左边的一段,中间的数会往第 n 个数移;如果删除的数在右边的一段,中间的数会往第 n+1 个数移。并且取掉这个数以后,原来被中点分成的两端现在仍然被中点隔开,也就是说,我们始终是在前一半取一个数,后一半取一个数,并且这两个数中至少有一个与中点相邻,所以我们把整个数列看成两个子数列 AB,其中 Aa1~anBa2n+1~an+2。我们每次进行的操作就是从一个数列中取出最后一个数,再从另外一个数列中删掉任意的一个数,询问最多取出多长的连续数字。

我们在完成一个取数然后抵消的事情,如果某个数列(假设是数列 A)底部的某个数在所枚举区间内,我们肯定要想办法把它拿出来,并且 B 中和这个要选的数抵消的数一定不能在区间内,那我们取哪个数比较好呢?显然,如果选较前的数,较后的数就容易跑到数列的末端,如果两个不在区间内的数都到了序列末端,那就必须取掉至少一个,就相当于浪费了这个原本可以抵消掉的数。所以我们应该每次尽量选靠后的数来抵消,所以每次取 B 最后一个不在区间内的数和 A 的最后一个抵消就好了。当然,如果 AB 尾端的两个数都不是区间内的数,那就只能被迫让它们互相抵消。所以如果枚举到某个在区间内的数,另一个数列中没有能与它抵消的了,这个区间就不满足答案应该有的性质;如果成功抵消完了,这个区间就是满足的。

然后似乎就不能在区间这个条件上动手脚,就只能枚举这段连续数字了,然后模拟上述操作,依次判断,时间复杂度 O(n3)

method 3 双指针

显然如果区间 [l,r] 是满足的,那么区间 [l+1,r][l,r1] 就都是满足的。对于这种性质,我们可以双指针维护,就可以不用分别枚举 lr 了,可以做到 O(n2)

method 4 线段树

发现双指针每次只修改一个数,如果每次都重新模拟一遍很亏,所以考虑能不能数据结构维护。

因为我们每次都是取最靠近 B 尾端的一个非区间内的数来抵消掉 A 的最后一个数,只考虑 A 在区间内数的抵消情况时,如果 A 数列中靠前的数都能有 B 数列中的数与其抵消,并且 B 数列中截至这一位不在区间内的数的数量大于等于 A 数列截至这一位在区间内的数的数量时,A 数列截至这一位都是能被抵消的,所以我们可以记录 A 数列在区间内的数的数量的前缀数组和 B 数列不在区间内的数的数量的前缀数组,保证 A 的前缀数组每一位都大于 B 的前缀数组。考虑 B 在区间内的数抵消情况同理,但是因为 AB 中的数的数量是相等的,所以如果 A 中在区间内的数的数量大于 B 中在不区间内的数的数量,那么 B 中在区间内的数的数量一定大于 A 中在不区间内的数的数量,所以考虑其中一个就可以了。

那么用线段树维护 A 的前缀数组与 B 的前缀数组的差,使其最小值保持在 0 以上即可,复杂度 O(nlogn)

代码

#include<bits/stdc++.h> #define max(a,b) ((a>b)?a:b) #define min(a,b) ((a<b)?a:b) using namespace std; const int N=4e5+50; int n; int a[N],loc[N]; int delt[N]; struct Tree { int l,r; int ans; int tag; }tr[N*4]; void pushup(int p) { tr[p].ans=min(tr[p<<1].ans,tr[(p<<1)|1].ans); } void pushdown(int p) { int tag=tr[p].tag; tr[p<<1].tag+=tag; tr[(p<<1)|1].tag+=tag; tr[p<<1].ans+=tag; tr[(p<<1)|1].ans+=tag; tr[p].tag=0; } void build(int l,int r,int p) { tr[p].l=l,tr[p].r=r; if(l==r) { tr[p].ans=l; return; } int mid=l+r>>1; build(l,mid,p<<1); build(mid+1,r,(p<<1)|1); pushup(p); } void change(int l,int r,int x,int p) { if(tr[p].l>=l&&tr[p].r<=r) { tr[p].ans+=x; tr[p].tag+=x; return; } pushdown(p); int mid=tr[p].l+tr[p].r>>1; if(mid>=l) change(l,r,x,p<<1); if(mid<r) change(l,r,x,(p<<1)|1); pushup(p); } int ans; int main() { scanf("%d",&n); for(int i=1;i<=n*2+1;++i) scanf("%d",&a[i]),loc[a[i]]=i; build(1,n,1); int l=1,r=0; while(r<=n*2+1) { if(tr[1].ans<0) { l++; if(loc[l-1]==n+1) continue; else if(loc[l-1]<n+1) change(loc[l-1],n,1,1); else change(2*n+2-loc[l-1],n,1,1); } else { ans=max(ans,r-l+1); r++; if(loc[r]==n+1) continue; else if(loc[r]<n+1) change(loc[r],n,-1,1); else change(2*n+2-loc[r],n,-1,1); } } printf("%d\n",ans); }

__EOF__

本文作者Foraino0267
本文链接https://www.cnblogs.com/vegtable-foraino/p/16250074.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Foraino0267  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示