【BJOI2019】删数 线段树
题目大意:一个数列若能在有限次数内删空,则称这个数列可以删空,一次删除操作定义如下:
记当前数列长度为k,则删掉数列中所有等于k的数。
现在有一个长度为n的数列a,有m次修改操作,为单点变值/整体增加或者减少1,问每次修改后,最少需要修改序列中多少个数,使得序列可以被删除。
数据范围:n≤150000。
我们首先考虑下最少需要修改的次数,我们设b[i]为数列a中填写了i的值得数量。
对于每一个i,我们可以用b[i]这么多数,覆盖区间[i−b[i]+1,i]。最终的答案就是未被覆盖的格子数量。
证明显然。
基于这个结论,我们就可以在O(n)的复杂度内求出一个序列a对应的答案,可以获得47分的好成绩。
在只有单点修改的情况下,我们发现我们可以用线段树做一下维护,就可以获得60分的好成绩。
我们发现,整体的+1或者−1,可以转化为查询的区间出现了移动,移动完查询区间后,我们更新一下两端的值即可。
这么搞就可以过掉这一题了。
时间复杂度:O(nlog n)。
1 #include<bits/stdc++.h> 2 #define M (1<<19) 3 using namespace std; 4 5 struct seg{int l,r,tag,minn,cnt;}a[M<<1]; 6 void pushup(int x){ 7 a[x].minn=min(a[x<<1].minn,a[x<<1|1].minn); 8 a[x].cnt=(a[x].minn==a[x<<1].minn?a[x<<1].cnt:0)+(a[x].minn==a[x<<1|1].minn?a[x<<1|1].cnt:0); 9 } 10 void upd(int x,int k){a[x].minn+=k; a[x].tag+=k;} 11 void pushdown(int x){if(a[x].tag) upd(x<<1,a[x].tag),upd(x<<1|1,a[x].tag); a[x].tag=0;} 12 13 void build(int x,int l,int r){ 14 a[x].l=l; a[x].r=r; if(l==r) return void(a[x].cnt=1); 15 int mid=(l+r)>>1; 16 build(x<<1,l,mid); build(x<<1|1,mid+1,r); 17 pushup(x); 18 } 19 void updata(int x,int l,int r,int k){ 20 if(l<=a[x].l&&a[x].r<=r) return upd(x,k); 21 pushdown(x); int mid=(a[x].l+a[x].r)>>1; 22 if(l<=mid) updata(x<<1,l,r,k); 23 if(mid<r) updata(x<<1|1,l,r,k); 24 pushup(x); 25 } 26 void updata(int x,int id,int k){return updata(x,id,id,k);} 27 int query(int x,int l,int r){ 28 if(a[x].minn>0) return 0; 29 if(l<=a[x].l&&a[x].r<=r) return a[x].cnt; 30 pushdown(x); int mid=(a[x].l+a[x].r)>>1,cnt=0; 31 if(l<=mid) cnt+=query(x<<1,l,r); 32 if(mid<r) cnt+=query(x<<1|1,l,r); 33 return cnt; 34 } 35 36 int num[M],n,q,m,orzorz[M*2]={0}; 37 int *cnt=orzorz+M; 38 int main(){ 39 scanf("%d%d",&n,&q); 40 for(int i=1;i<=n;i++) scanf("%d",num+i),cnt[num[i]]++; 41 int T=max(n,q),m=T+n+q+2,move=0; 42 build(1,1,m); 43 for(int i=1;i<=n;i++) updata(1,T+i-cnt[i]+1,T+i,1); 44 while(q--){ 45 int p,x; scanf("%d%d",&p,&x); 46 if(p>0){ 47 x-=move; 48 if(num[p]+move>=1&&num[p]+move<=n) 49 updata(1,num[p]-cnt[num[p]]+1+T,-1); 50 cnt[num[p]]--; num[p]=x; cnt[num[p]]++; 51 updata(1,num[p]-cnt[num[p]]+1+T,1); 52 }else{ 53 if(x<0) move--; 54 updata(1,-move-cnt[-move]+1+T,-move+T,x); 55 updata(1,n-move-cnt[n-move]+1+T,n-move+T,-x); 56 if(x>0) move++; 57 } 58 printf("%d\n",query(1,T-move+1,T-move+n)); 59 } 60 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!