传送门(权限)

传送门(非权限)

题解

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

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

  然后怎么做呢?

  先把题设式子变形一下$$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. VUE项目 - IE报vuex requires a Promise polyfill in this browser问题解决

    第一步: 安装 babel-polyfill . babel-polyfill可以模拟ES6使用的环境,可以使用ES6的所有新方法 npm install --save babel-polyfill ...

  2. JS计算字符长度、字节数 -- 转

    一个汉字在UTF-8编码中占用几个字节? 占用3个字节的范围 U+2E80 - U+2EF3 : 0xE2 0xBA 0x80 - 0xE2 0xBB 0xB3 共 115 个 U+2F00 - U+ ...

  3. Maven编译并打包Mahout CDH版源码

    目录 1. 问题描述 最近在使用Mahout里的推荐算法进行实验,由于业务需求,需要修改Mahout源码,将原本输出到HDFS上的结果输出到HBase中.由于Mahout发布的源码都是Maven项目, ...

  4. http post Content-type: application/json; charset=utf-8

      The header just denotes what the content is encoded in. It is not necessarily possible to deduce t ...

  5. spring集成mybatis配置多个数据源,通过aop自动切换

    spring集成mybatis,配置多个数据源并自动切换. spring-mybatis.xml如下: <?xml version="1.0" encoding=" ...

  6. .net core利用MySqlBulkLoader大数据批量导入MySQL

    最近用core写了一个数据迁移小工具,从SQLServer读取数据,加工后导入MySQL,由于数据量太过庞大,数据表都过百万,常用的dapper已经无法满足.三大数据库都有自己的大数据批量导入数据的方 ...

  7. java类加载器的一些测试

    package classloader; import java.lang.reflect.Method; import org.junit.Test; import com.example.Samp ...

  8. parseInt(string, radix)

    参数 描述 string 必需.要被解析的字符串. radix 可选.表示要解析的数字的基数.该值介于 2 ~ 36 之间. 如果省略该参数或其值为 0,则数字将以 10 为基础来解析.如果它以 “0 ...

  9. UISearchDisplayController

    // // FirstViewController.swift // SearchDisplayDemo // // Created by Bruce Lee on 24/12/14. // Copy ...

  10. (广搜)可口可乐 -- hdu -- 1495

    链接: http://acm.hdu.edu.cn/showproblem.php?pid=1495 Time Limit: 2000/1000 MS (Java/Others)    Memory ...