传送门(权限)

传送门(非权限)

题解

  我终终终终终终于做出来啦!!!

  作为一个没有学过替罪羊树的蒟蒻现场学了一下替罪羊树,作为一个平衡树都写数组版本的看着大佬的指针题解无语只能硬去理解然后照着抄了一波指针

  然后怎么做呢?

  先把题设式子变形一下$$dist(i,j)\leq r_i+r_j$$

  $$dist(i,LCA)+dist(LCA,j)\leq r_i+r_j$$

  $$r_i-dist(i,LCA)\geq dist(j,LCA)-r_j$$

  然后我们在每一个点开两棵平衡树,分别维护以$i$为根的子树中$dist(i,u)-r_u$和$dist(fa[i],u)-r_u$的值。然后每一次跳点分树时,记录$r_i-dist(i,LCA)+1$,在平衡树里查询有多少个数小于它就好了,修改直接往上跳,不断改

  然而如果原树是一条链怎么办?强制在线,必然会被卡成$O(n^2)$,怎么办?

  我们联想一下替罪羊树的思想,如果点分树上某一个点的子树过大,直接拍扁重建。联想一下替罪羊树,可以发现时间复杂度是能得到保证的,这样可以保证时间复杂度是$O(nlogn)$。

  因为蒟蒻是第一次写替罪羊树&&第一次码这么长的代码,于是加了一堆注释,米娜应该能够看懂吧……

 // luogu-judger-enable-o2
//minamoto
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#define ll long long
#define inf 1000000000
#define N 100005
#define alpha 0.755
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,:;}
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
char sr[<<],z[];int C=-,Z;
inline void Ot(){fwrite(sr,,C+,stdout),C=-;}
inline void print(ll x){
if(C><<)Ot();if(x<)sr[++C]=,x=-x;
while(z[++Z]=x%+,x/=);
while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
int n,e,head[N],Next[N<<],ver[N<<],val[N];
inline void add(int u,int v){
//加边,构建原树
ver[++e]=v,Next[e]=head[u],head[u]=e;
ver[++e]=u,Next[e]=head[v],head[v]=e;
}
vector<int> to[N];
int f[N][],bin[],tp,dep[N],len[N];
inline int LCA(int a,int b){
if(dep[a]<dep[b]) a^=b^=a^=b;
int i,cha=dep[a]-dep[b];
for(i=tp;~i;--i) if(cha&bin[i]) a=f[a][i];
if(a==b) return a;
for(i=tp;~i;--i) if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
return f[a][];
}
inline int dis(int a,int b){return len[a]+len[b]-(len[LCA(a,b)]<<);}
struct Goat{
int val,sz;Goat *ch[];
Goat(){}
inline bool bad(){
//判断是否某个子树过大
return ch[]->sz>=sz*alpha+||ch[]->sz>=sz*alpha+;
}
}*tree1[N],*tree2[N],mem[N<<],*pool[N<<],*null,*sta[N];
int tot,top;
void init(){
//构建内存池,避免动态开点时间复杂度太大
null=new Goat();
null->ch[]=null->ch[]=null,null->val=null->sz=;
for(int i=;i<(N<<);++i) pool[i]=mem+i;
tot=(N<<)-;
for(int i=;i<=n;++i) tree1[i]=tree2[i]=null;
}
Goat** insert(Goat *&a,int val){
//插入节点,并判断是否有子树过大
//注意要开引用
if(a==null){
a=pool[tot--],a->ch[]=a->ch[]=null;
a->val=val,a->sz=;return &null;
}
++a->sz;
//小于等于往左插,大于往右插
Goat **o=insert(a->ch[a->val<val],val);
if(a->bad()) o=&a;return o;
}
int getrk(Goat *o,int val){
//查找有多少比val小的数
if(o==null) return ;
return (o->val>=val)?getrk(o->ch[],val):(getrk(o->ch[],val)+o->ch[]->sz+);
}
void Erholung(Goat *o){
//清除节点,回收内存池
if(o==null) return;
if(o->ch[]!=null) Erholung(o->ch[]);
pool[++tot]=o;
if(o->ch[]!=null) Erholung(o->ch[]);
}
void travel(Goat *o){
//暴力重构整棵树(递归找节点)
if(o==null) return;
if(o->ch[]!=null) travel(o->ch[]);
sta[++top]=o;
if(o->ch[]!=null) travel(o->ch[]);
}
Goat* build(int l,int r){
//重构
if(l>r) return null;
int mid=l+r>>;
Goat *o=sta[mid];o->sz=r-l+;
o->ch[]=build(l,mid-),o->ch[]=build(mid+,r);
return o;
}
inline void rebuild(Goat *&o){top=,travel(o),o=build(,top);};
inline void Insert(Goat *&a,int val){
//同,这里和上面rebuild也要开引用
Goat **o=insert(a,val);
if(*o!=null) rebuild(*o);
}
int sz[N],son[N],size,rt,fa[N];bool vis[N];
void findrt(int u,int fa){
sz[u]=,son[u]=;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v!=fa&&!vis[v]){
findrt(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]);
}
}
cmax(son[u],size-sz[u]);
if(son[u]<son[rt]) rt=u;
}
void dfs(int u,int f,int rt){
//遍历子树,把所有的东西都插到平衡树里
Insert(tree1[rt],dis(u,rt)-val[u]);
if(fa[rt]) Insert(tree2[rt],dis(u,fa[rt])-val[u]);
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v!=f&&!vis[v]) dfs(v,u,rt);
}
}
void solve(int u,int f){
fa[u]=f,vis[u]=;
int totsz=size;
dfs(u,,u);
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(!vis[v]){
rt=,size=sz[v]>sz[u]?totsz-sz[u]:sz[v];
findrt(v,),to[u].push_back(rt),solve(rt,u);
}
}
}
void recover(int x){
//遍历点分树,清空节点
++size,vis[x]=;
Erholung(tree1[x]),Erholung(tree2[x]);
tree1[x]=tree2[x]=null;
for(int i=,k=to[x].size();i<k;++i) recover(to[x][i]);
to[x].clear();
}
void rebuild(int x){
//点分树某一子树过大,重构
size=,recover(x),rt=,findrt(x,);
if(fa[x])
for(int i=,j=to[fa[x]].size();i<j;++i)
if(to[fa[x]][i]==x) to[fa[x]][i]=rt;
solve(rt,fa[x]);
}
ll ans=;
int insert(int x){
register int i,ds,res=;
//求出小于等于val[x]-dis(x,fa[i])的个数,只要在平衡树里找小于val[x]-dis(x,fa[i])+1的就可以了
for(i=x;fa[i];i=fa[i])
ds=val[x]-dis(x,fa[i])+,ans+=getrk(tree1[fa[i]],ds)-getrk(tree2[i],ds);
Insert(tree1[x],-val[x]);
//然后维护修改
for(i=x;fa[i];i=fa[i]){
int dist=dis(fa[i],x)-val[x];
Insert(tree1[fa[i]],dist);
Insert(tree2[i],dist);
}
//考虑是否需要拍扁重建
for(i=x;fa[i];i=fa[i])
if(tree1[i]->sz>=tree1[fa[i]]->sz*alpha+) res=fa[i];
return res;
}
int main(){
n=read(),n=read();
register int i,j,b,x;
for(bin[]=i=;i<=;++i) bin[i]=bin[i-]<<;
while(bin[tp+]<=n) ++tp;
son[]=n+,rt=,init();
for(int i=;i<=n;++i){
fa[i]=f[i][]=read()^(ans%inf),b=read(),val[i]=read();
dep[i]=dep[f[i][]]+,len[i]=len[f[i][]]+b,vis[i]=;
if(fa[i]) to[fa[i]].push_back(i),add(f[i][],i);
for(j=;bin[j]+<=dep[i];++j) f[i][j]=f[f[i][j-]][j-];
x=insert(i);if(x) rebuild(x);
print(ans);
}
Ot();
return ;
}

bzoj3435 [Wc2014]紫荆花之恋(动态点分治+替罪羊树)的更多相关文章

  1. BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树)

    题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...

  2. luoguP3920 [WC2014]紫荆花之恋 动态点分治 + 替罪羊树

    意外的好写..... 考虑点分 \(dis(i, j) \leq r_i + r_j\) 对于过分治中心一点\(u\),有 \(dis(i, u) - r_i = dis(j, u) + r_j\) ...

  3. [WC2014]紫荆花之恋(动态点分治+替罪羊思想)

    题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...

  4. UOJ #55 & 洛谷 P3920 紫荆花之恋 —— 动态点分治+替罪羊树

    题目:http://uoj.ac/problem/55 https://www.luogu.org/problemnew/show/P3920 参考博客:https://www.cnblogs.com ...

  5. uoj 55 紫荆花之恋 动态点分治+替罪羊式重构+treap

    每插入一个点,直接把它当做重心插入原树,当做是动态点分树一样维护 但这样深度会越来越大,所以我们用类似替罪羊的方法 当树失去平衡时,对子树进行一次点分,保证复杂度 #include <cstdi ...

  6. 【BZOJ3435】[Wc2014]紫荆花之恋 替罪点分树+SBT

    [BZOJ3435][Wc2014]紫荆花之恋 Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从 ...

  7. 【bzoj3435】[Wc2014]紫荆花之恋 替罪点分树套SBT

    题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...

  8. BZOJ3435: [Wc2014]紫荆花之恋(替罪羊树,Treap)

    Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是 ...

  9. bzoj3435 [Wc2014]紫荆花之恋

    如果这棵树不变的话,就是一个裸的点分树套平衡树,式子也很好推$di+dj<=ri+rj$,$ri-di>=dj-rj$ 平衡树维护$dj-rj$,然后查$ri-di$的$rank$即可. ...

随机推荐

  1. IOS调试技巧:当程序崩溃的时候怎么办 xcode调试

    转自:http://www.ityran.com/archives/1143 ------------------------------------------------ 欢迎回到当程序崩溃的时候 ...

  2. epoll用法【整理】

    l  epoll是什么? epoll是当前在Linux下开发大规模并发网络程序的热门人选,epoll 在Linux2.6内核中正式引入,和select相似,都是I/O多路复用(IO multiplex ...

  3. ansible 使用

    批量添加ssh免密 ansible mhc -m authorized_key -a "user=root key='{{ lookup('file','/root/.ssh/id_dsa. ...

  4. 股票F10

    [股票F10] 股票非行情类的基本面资料统称为股票F10   在各种金融行情终端软件中,用户通过键盘上的F10快捷键,可迅速查看上市公司的非行情信息,诸如:公司概况.财务数据.公司公告.公司新闻.经营 ...

  5. python常用option

    [python常用option] 1. -c cmd : program passed in as string (terminates option list) 解析字符串命令,不读cmd之后的op ...

  6. pymysql.err.IntegrityError: (1062, "Duplicate entry 'roxml-ROXML' for key 'PRIMARY'")

    在<Python数据挖掘-概念.方法与实践>一书的第3章实体匹配中,如果一路按照作者的代码及SQL语句进行配置运行的话,会出现如题目所示的错误.根据python脚本的执行错误提示显示,错误 ...

  7. javascript的构造函数和实例对象、prototype和__proto__的区别,原型对象及构造器的理解

    一.前言 我们先通过代码来分别打印出实例对象.构造函数,以及修改了原型对象的构造函数,通过对比内部结构来看看他们之间的区别. //定义构造函数 function Person(name, age){ ...

  8. jquery记录

    jquery validate验证框架 参考:http://www.cnblogs.com/linjiqin/p/3431835.html http://www.runoob.com/jquery/j ...

  9. Spring JMX之三:通知的处理及监听

    通过查询MBean获得信息只是查看应用状态的一种方法.但当应用发生重要事件时,如果希望 能够及时告知我们,这通常不是最有效的方法. 例如,假设Spittr应用保存了已发布的Spittle数量,而我们希 ...

  10. Java Thread系列(三)线程安全

    Java Thread系列(三)线程安全 一.什么是线程安全 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的. 线程安全来 ...