博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[JZOJ3691] 【CF414E】Mashmokh's Designed tree
阅读量:5292 次
发布时间:2019-06-14

本文共 3283 字,大约阅读时间需要 10 分钟。

题目大意

给你一棵树,接下来对这棵树进行三种操作:

1、询问两点之间的距离。
2、让某个点变为它原来的第\(h\)个祖先的最后一个儿子。
3、求\(dfs\)序中最后一个深度为\(k\)的点。


正解

第一种是Cold_Chair大爷提出来的\(LCT\)维护\(ETT\)的做法。

具体怎样就不说了……据说代码5000+

第二种就直接是\(ETT\)了(其实这是一道ETT的板题啊)

对于入栈点(记作\(l\))打个\(+1\),对于出栈点(记作\(r\))打个\(-1\),维护前缀和。这个前缀和相当于深度。
这里维护前缀和的方法比较巧妙,不需要区间修改,具体见程序。
对于第一个操作,找到\(l_u\)\(l_v\)之间最小的深度。显然这个深度就是它们的\(LCA\)的深度。
对于第二个操作,找第\(h\)个祖先的时候先求出祖先的深度,然后在前面找最靠右的深度为它的点。显然前缀和是连续的,所以可以在\(splay\)上二分。如果它是入栈点,就是要找的那个祖先;如果是出栈点,那它的父亲就是那个祖先。
对于第三个操作,直接找最后面前缀和为\(k\)的点。和操作二一样。


代码

using namespace std;#include 
#include
#include
#include
#include
#include
#include
#define N 100010#define INF 1000000000int n,m;vector
to[N];struct Node *null,*root;struct Node{ Node *fa,*c[2]; int val,sum,mx,mn; inline void init(int _val){ fa=c[0]=c[1]=null; val=sum=mx=mn=_val; } inline void update(){ sum=c[0]->sum+c[1]->sum+val; mx=max(max(c[0]->mx,c[0]->sum+val+c[1]->mx),c[0]->sum+val); mn=min(min(c[0]->mn,c[0]->sum+val+c[1]->mn),c[0]->sum+val); } inline bool getson(){return fa->c[0]!=this;} inline void rotate(){ Node *y=fa,*z=y->fa; if (z!=null) z->c[y->getson()]=this; bool k=getson(); fa=z; y->c[k]=c[k^1],c[k^1]->fa=y; c[k^1]=y,y->fa=this; sum=y->sum,mx=y->mx,mn=y->mn; y->update(); } inline void splay(Node *t){ while (fa!=t){ if (fa->fa!=t) getson()!=fa->getson()?rotate():fa->rotate(); rotate(); } if (t==null) root=this; }} in[N],out[N],*beg,*end;inline Node *nxt(Node *t,bool dir){ t->splay(null); Node *res=t->c[dir]; for (;res->c[dir^1]!=null;res=res->c[dir^1]); return res;}inline void push_back(Node *x){ Node *t=root; for (;t->c[1]!=null;t=t->c[1]); t->splay(null); t->c[1]=x,x->fa=t; t->update();}inline Node *split(Node *l,Node *r){ l=nxt(l,0),r=nxt(r,1); r->splay(null),l->splay(r); Node *t=l->c[1]; l->c[1]=null,t->fa=null; l->update(),r->update(); return t;}inline void insert(Node *t,Node *p){ p->splay(null); Node *pre=nxt(p,0); pre->splay(p); pre->c[1]=t,t->fa=pre; pre->update(),p->update();}inline Node *find_last(Node *t,int k){ t->splay(null); Node *x=t->c[0],*res=null; while (1){ if (x->c[1]!=null && x->c[1]->mn<=k-x->c[0]->sum-x->val && k-x->c[0]->sum-x->val<=x->c[1]->mx){ k-=x->c[0]->sum+x->val; x=x->c[1]; continue; } else if (x->c[0]->sum+x->val==k) return x; x=x->c[0]; } return res;}void dfs(int x){ in[x].init(1); push_back(&in[x]); for (int i=0;i
init(0),root=beg; dfs(1); end=new Node,end->init(0),push_back(end); for (int i=1;i<=m;++i){ int op; scanf("%d",&op); if (op==1){ int u,v,depu,depv,deplca; scanf("%d%d",&u,&v); if (u==v){ printf("0\n"); continue; } in[u].splay(null),in[v].splay(&in[u]); if (in[v].getson()==0) swap(u,v); Node *l=nxt(&in[u],0),*r=nxt(&in[v],1); r->splay(null),l->splay(r); deplca=l->c[0]->sum+l->val+l->c[1]->mn; depu=l->c[0]->sum+l->val+1; depv=l->sum; printf("%d\n",depu+depv-deplca*2); } else if (op==2){ int v,h,anc; scanf("%d%d",&v,&h); in[v].splay(null); Node *tmp=find_last(&in[v],in[v].c[0]->sum+1-h); fa[v]=anc=(tmp->val==-1?fa[tmp-out]:tmp-in); Node *t=split(&in[v],&out[v]); insert(t,&out[anc]); } else{ int k; scanf("%d",&k); Node *tmp=find_last(end,k+1); printf("%d\n",tmp->val==-1?fa[tmp-out]:tmp-in); } } return 0;}

总结

\(LCT\)搞不了的东西,要试试\(ETT\)

转载于:https://www.cnblogs.com/jz-597/p/11420897.html

你可能感兴趣的文章
五子棋项目的实现(二)博弈树算法的描述
查看>>
Hibernate : Disabling contextual LOB creation as createClob() method threw error
查看>>
【bzoj4872】[Shoi2017]分手是祝愿 期望dp
查看>>
字符串元转分
查看>>
thinkphp 防sql注入
查看>>
201521123044 《Java程序设计》第1周学习总结
查看>>
MIT Scheme 的基本使用
查看>>
程序员的“机械同感”
查看>>
在16aspx.com上下了一个简单商品房销售系统源码,怎么修改它的默认登录名和密码...
查看>>
c++回调函数
查看>>
linux下Rtree的安装
查看>>
【Java】 剑指offer(53-2) 0到n-1中缺失的数字
查看>>
Delphi中ListView类的用法
查看>>
bzoj3110: [Zjoi2013]K大数查询 【树套树,标记永久化】
查看>>
[原创]Java 的传值小例子
查看>>
【MySQL学习】安装和配置 服务无法启动 没有报告任何错误
查看>>
C# 修饰符
查看>>
JavaScript启示录
查看>>
我需要什么样的浏览器?
查看>>
取textaera里的值
查看>>