关于treap启发式合并的一点脑洞(以bzoj2809为例)
来源:互联网 发布:java课程设计案例源码 编辑:程序博客网 时间:2024/04/28 21:10
首先我知道bzoj2809正解应该是可并堆,之所以写treap启发式合并单纯只是因为这个脑洞…
首先我们有两个treap,分别是 A 和 B ,它们的节点数分别为 n 和 m
网上普通的启发式合并是把 A 中的结点一个个扔进 B 里,这样复杂度是
然而 zyqn 告诉我 myy 说这个事情可以做到
因为这意味着,如果你有 n 个只有一个结点的 treap,然后我每次指定两个让你合并,顺便询问询问,最后合并成一个 treap, 那么这个事情可以做到
然后我搞了搞发现我可能写挂了,不过还是决定放上来讲讲。
具体来说是把 B 那个 treap 拆成一个序列,然后塞进 A 相应的地方。就像这样:
但是光这么搞会出一些问题,比如有 n 个结点,权值分别为 1~n ,那把第二个,第三个…第 n 个点依次塞进第一个点的 treap 里,这个 treap 就会变成一条链。补救办法是在某些时候把需要 merge 的 A 和 B 两个 treap 都拆成序列,然后暴力重构整个 treap 的这个子树。
然后问题就是什么时候暴力重构了,这个可以参考普通 treap,如果两个 treap 的结点数分别为 n 和 m
这么一通搞完以后我发现复杂度我并不会证。也许是
不过这样写有一个好,就是如果这个 treap 不需要可持久化的话,空间复杂度是 但是要多写这么多函数啊喂,如果是OI比赛调不出来怎么办
附代码
#include <cstdio>#include <algorithm>#define N 100050#define NDS 1000050#define INF 1000000001#define swap(a,b) std::swap(a,b)//namespace fastin//{ #define BUF 100005 char __s[BUF],*_ss,*_st; inline char gchar() { if(_ss==_st) _st=(_ss=__s)+fread(__s,sizeof(char),BUF-5,stdin); return *_ss++; } inline int RD() { int res;char cr; while( (cr=gchar())<'0' || cr>'9');res=cr-'0'; while( (cr=gchar())>='0' && cr<='9') res=res*10+cr-'0'; return res; } #undef BUF//}int trand(){ static long long x=2947183,a=3798293,b=1000000009; return x=(x*a+b)&0x7fffffff;}int trand(int lim){return trand()%lim;}#define ls p->son[0]#define rs p->son[1]struct Node *null;struct Node{ int sz,val; long long totval; Node *son[2]; void update() { sz=(son[0]->sz)+(son[1]->sz)+1; totval=(son[0]->totval)+(son[1]->totval)+val; if(totval>INF) totval=INF; }}_pol[NDS],*_ndcnt;Node* newNode(){return _ndcnt++;}void init(){ null=new Node(); *null=(Node){0,0,0,{NULL,NULL}}; _ndcnt=_pol;}Node *que[N],*q2[N];int ql,qr,q2top;void takeApart(Node *p)//把treap p拆成序列{ if(p==null) return; takeApart(ls); que[++qr]=p; takeApart(rs);}void combine(Node *p,int &l,int r)//把treap p拆成序列,并且和que里的序列合成一个新序列{ if(p==null) return; combine(ls,l,r); while(l<=r && (que[l]->val)<(p->val)) q2[++q2top]=que[l++]; q2[++q2top]=p; combine(rs,l,r);}Node* rebuild(int l,int r)//暴力重建,把序列变成一个treap{ if(l>r) return null; int mid=(l+r)>>1;// Node *p=newNode();*p=*q2[mid]; Node *p=q2[mid]; ls=rebuild(l,mid-1); rs=rebuild(mid+1,r); p->update(); return p;}Node* rebuild(Node *p,int l,int r)//暴力重建treap p和序列que(中的[l,r]这一段){ q2top=0; combine(p,l,r); while(l<=r) q2[++q2top]=que[l++]; return rebuild(1,q2top);}int find(int l,int r,int val){ //判定merge下传的时候应该把que序列中的前一半扔给左儿子,后一半扔给右儿子 //这个函数是用来找前一半和后一半的分界点的 //不要问我为什么不直接二分 for(int del=0;;del++) { if(val<(que[l+del]->val)) return l+del-1; if(val>=(que[r-del]->val)) return r-del; }}Node* merge(Node *rt,int l,int r){ //合并treap rt和序列que(中的[l,r]这一段)。这个代码的核心。// Node *p=newNode();*p=*rt; Node *p=rt; if(l>r) return p; if(p==null) return rebuild(p,l,r); int size1=p->sz,size2=r-l+1; if(size1<=size2 || trand(size1+size2)<size2) return rebuild(p,l,r); int mid=find(l,r,p->val); ls=merge(rt->son[0],l,mid); rs=merge(rt->son[1],mid+1,r); p->update(); return p;}Node* merge(Node *a,Node *b)//合并a,b两个treap,其中b被拆成序列{ if((a->sz)<(b->sz)) swap(a,b); ql=1;qr=0; takeApart(b); return merge(a,ql,qr);}/*int query(Node* p,int maxcost){ if(p==null) return 0; if(p->totval<=maxcost) return p->sz; if(ls->totval<=maxcost) { if((ls->totval)+(p->val)<=maxcost) return ls->sz+1+query(rs,(maxcost)-(ls->totval)-(p->val)); else return ls->sz; } else return query(ls,maxcost);}*/int query(Node* &p,int maxcost)//本来应该用上面那个query(),这个函数是用题目性质加了一些常数优化{ if(p==null) return 0; if(p->totval<=maxcost) return p->sz; int res; if(ls->totval<=maxcost) { if((ls->totval)+(p->val)<=maxcost) res=ls->sz+1+query(rs,(maxcost)-(ls->totval)-(p->val)); else res=ls->sz,p=ls; } else res=query(ls,maxcost),p=ls; if(p!=null) p->update(); return res;}int fa[N],cost[N],leadership[N];int fson[N],bro[N];Node *rot[N];int main(){ init(); int n,maxcost;n=RD();maxcost=RD(); for(int i=1;i<=n;i++) { fa[i]=RD();cost[i]=RD();leadership[i]=RD(); bro[i]=fson[fa[i]]; fson[fa[i]]=i; } long long ans=0; for(int i=n;i>=1;i--) { rot[i]=newNode(); *rot[i]=(Node){1,cost[i],cost[i],{null,null}}; for(int j=fson[i];j;j=bro[j]) rot[i]=merge(rot[i],rot[j]); long long res=(long long)leadership[i]*query(rot[i],maxcost); if(res>ans) ans=res; } printf("%lld\n",ans);}
- 关于treap启发式合并的一点脑洞(以bzoj2809为例)
- treap启发式合并(BZOJ2809)||splay||左偏树
- BZOJ2809可合并堆或启发式合并
- 启发式合并&线段树合并&treap合并
- Poj 1741——treap的启发式合并
- BZOJ 2733 HNOI2012 永无乡 Treap+启发式合并
- BZOJ 3545 ONTAK2010 Peaks Treap启发式合并
- BOZJ2733 [HNOI2012]永无乡(Treap+启发式合并)
- BZOJ 3545 [ONTAK2010]Peaks Treap启发式合并
- 【bzoj2733】【HNOI2012】【永无乡】【treap+启发式合并】
- 【BZOJ 2733】[HNOI2012]永无乡 启发式合并treap
- BZOJ 2733: [HNOI2012]永无乡 (Treap+启发式合并)
- BZOJ 2733([HNOI2012]永无乡-Treap启发式合并)
- [BZOJ2733][HNOI2012]永无乡-Treap-启发式合并
- 关于多维数组的一点个人的理解(以三维数组为例)
- BZOJ 2809 APIO2012 dispatching Treap+启发式合并 / 可并堆
- 2809: [Apio2012]dispatching 启发式合并treap 可并堆
- 3545: [ONTAK2010]Peaks 启发式合并treap 离线处理
- tensorflow线性回归
- 魔法照片
- META是什么意思?
- log4j flush
- JMeter入门
- 关于treap启发式合并的一点脑洞(以bzoj2809为例)
- 通过MAT来进行内存泄露的分析
- 数据结构中缀表达式转后缀表达式以及后缀转中缀表达式
- Spring Boot 菜鸟教程 23 站点地图sitemap.xml
- div style常用属性介绍及使用示例
- myeclipse2015修改web项目部署名
- 数据存储:从pickle到sqlite 《Head First Python》第九章
- oj-1-蛇形填数
- Android中的几种内存泄露情况总结