主席树Ⅱ
2021/8/18 23:12:38
本文主要是介绍主席树Ⅱ,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
神秘数
一道算是十分巧妙的题目。
与其它许多题目一样,一开始并不会想到用什么主席树,那些都只是后来用来做优化的东西。让我们来思考一下,假如给一个数列,然后只有一次询问的话应该怎么办?排一下序,然后从小到大扫描。从小往大扫描的过程中,如果当前可以表示的范围是 \([1,able]\) ,当前这个可利用的数是 \(now\) ,那么很容易可以得到,如果 \(able+1\)<\(now-1\) 的话加入一个新的数必然会导致 \([able+1,now-1]\) 的数无法表示,反之,就把 \(able\) 修改成 \(able+now\) 。然后就可以啦。
但这样似乎是 \(O(NlogN)\) 的算法,如果硬来的话,势必会导致算法成为 \(MNlogN\) 完全过不了。所以我们需要加速这个过程。一般来说,导致这种情况的根源就是它指针是一个一个往后挪的,这非常浪费时间(想起了 \(KMP\) 算法……),我们就可以想一下,如何加速这个过程。
上面也说了,一个元素 \(now\) 可以更新 \(able\) 的条件是 \(able+1\)<\(now-1\) ,也就是说 \(\forall now\in[1,able+1]\) 都可以更新 \(able\) ,相当于每次我们进行的操作就变成了 \(able=able+\sum{now\in[last,able+1]}\),其中 \(last\) 表示上一次的 \(able\) 这样是为了防止同一个元素被加很多次。
可证这样的算法是 \(MlogN^2\) 的。但这还只是静态问题,我们需要进行区间查询,而这种比较复杂的区间信息查询显然无法用线段树实现,要么用分块,要么用莫队离线求,要么用主席树,这里选用了主席树。然后就好办啦。
#include<cstdio> //#define zczc const int N=100010; const int S=1e9; inline void read(int &wh){ wh=0;int f=1;char w=getchar(); while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();} while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); } wh*=f;return; } #define lc t[wh].left #define rc t[wh].right #define mid (l+r>>1) struct node{ int left,right,data; }t[N<<5]; int nodecnt; inline void pushup(int wh){ t[wh].data=t[lc].data+t[rc].data; return; } inline int change(int x,int l,int r,int pl){ int wh=++nodecnt; lc=t[x].left,rc=t[x].right,t[wh].data=t[x].data; t[wh].data+=pl; if(l==r){ return wh; } if(pl<=mid)lc=change(lc,l,mid,pl); else rc=change(rc,mid+1,r,pl); //pushup(wh); //printf("c:%d %d %d %d %d %d\n",x,wh,l,r,pl,t[wh].data); return wh; } inline int work(int lwh,int rwh,int l,int r,int wl,int wr){ //printf("%d %d %d %d %d %d\n",lwh,rwh,l,r,wl,wr); if(wl>wr)return 0; if(t[rwh].data-t[lwh].data==0)return 0; if(wl<=l&&r<=wr)return t[rwh].data-t[lwh].data; int an=0; if(wl<=mid)an+=work(t[lwh].left,t[rwh].left,l,mid,wl,wr); if(wr>mid)an+=work(t[lwh].right,t[rwh].right,mid+1,r,wl,wr); return an; } #undef mid #undef lc #undef rc int root[N],m,n; signed main(){ #ifdef zczc freopen("in.txt","r",stdin); #endif int s1,s2; read(m); for(int i=1;i<=m;i++){ read(s1); root[i]=change(root[i-1],1,S,s1); //printf("now:%d\n",root[i]); } read(n); while(n--){ read(s1);read(s2); int maxn=0,ans=0,sum=0; while(true){ sum=work(root[s1-1],root[s2],1,S,maxn+1,ans+1); //printf("work:%d %d %d\n",maxn+1,ans+1,sum); if(sum==0)break; maxn=ans+1;ans+=sum; } printf("%d\n",ans+1); } return 0; }
这篇关于主席树Ⅱ的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2025-01-09百万架构师第十二课:源码分析:Spring 源码分析:Spring系统概述及IOC实现原理|JavaGuide
- 2025-01-08如何用关键链方法突破项目管理瓶颈?
- 2025-01-08电商人必看!6 款提升团队协作与客户满意度软件!
- 2025-01-08电商团队管理混乱?快用这 6 款软件优化协作流程!
- 2025-01-08短剧制作效率低?试试这5款任务管理工具
- 2025-01-08高效应对电商高峰,6 款团队协作软件大揭秘!
- 2025-01-08为什么外贸人都爱上了在线协作工具?
- 2025-01-08提升工作效率,从这些任务管理工具开始
- 2025-01-08新年电商订单暴增,必备的 6 款可视化协作办公软件有哪些?
- 2025-01-08短剧制作经理必备技能与工具全解析