题目描述

强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点。每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 i 都有一个感受能力值Ri ,小精灵 i, j 成为朋友当且仅当在树上 i 和 j 的距离 dist(i,j) ≤ Ri + R! ,其中 dist(i, j)表示在这个树上从 i 到 j 的唯一路径上所有边的边权和。强强和萌萌很好奇每次新长出一个叶子节点之后,这个树上总共有几对朋友。  
我们假定这个树一开始为空,节点按照加入的顺序从 1开始编号。由于强强非常好奇, 你必须在他每次出现新节点后马上给出总共的朋友对数,不能拖延哦。

输入

共有 n + 2 行。 
第一行包含一个正整数,表示测试点编号。 
第二行包含一个正整数 n ,表示总共要加入的节点数。 
我们令加入节点前的总共朋友对数是 last_ans,在一开始时它的值为0。 
接下来 n 行中第 i 行有三个数 ai, bi, ri,表示节点  i  的父节点的编号为 ai xor (last_ans mod 10^9)   (其中xor 表示异或,mod  表示取余,数据保证这样操作后得到的结果介于 1到i  –  1之间),与父节点之间的边权为 ci,节点 i 上小精灵的感受能力值为r!。 
注意 a1 = c1 = 0,表示 1 号点是根节点,对于 i > 1,父节点的编号至少为1。

输出

包含 n 行,每行输出1 个整数, 表示加入第 i 个点之后,树上有几对朋友。

样例输入

0
5
0 0 6
1 2 4
0 9 4
0 5 5
0 2 4

样例输出

0
1
2
4
7

提示

1<=Ci<=10000
Ai<=2*10^9
Ri<=10^9
N<=100000

经过一天的卡常+优化,经过从非旋转treap到旋转treap到替罪羊树的改进,终于过掉了这个传说中的神仙题!

因为这道题比较繁琐,我们分块讲解。

点分治

按照动态点分治的常规套路,我们考虑对于一棵给定的树,单次询问如何用点分治处理。

当以x为分治中心时,如果联通块中i,j两个点满足条件,假设i,j距离分治中心距离分别为di,dj,那么di+dj<=ri+rj,即di-ri<=rj-dj。

那么遍历分治中心的每个子树,记录每个点的ri-di,对于每个点x找到有多少个点的ri-di>=dx-rx,同样容斥去掉两点位于同一棵子树中的答案。

对于维护每个点的ri-di只要开一棵平衡树即可(因为本题强制在线不能离散化,所以不能使用权值线段树)。

平衡树

因为本题用到的平衡树功能单一,所以建议使用高速平衡树(除非旋转treap和splay之外的其他平衡树)。

当然如果你有高超的卡常技巧也可以用splay或非旋转treap。

对于高速平衡树的选择,个人建议选择插入时间复杂度较优的SBT或替罪羊树(原因后面会说)。

对于替罪羊树,经过不懈尝试,我发现重构因子为0.86时的时间复杂度比较优秀。

动态点分治&点分树

多次询问显然要用点分树来解决,通过上面对单次查询点分治的处理方法,我们可以知道点分树上每个点需要维护的信息:

1、每个点开一棵平衡树维护以它为分治中心时联通块内所有点的ri-di(di为联通块内点为到它的距离)。

2、每个点开一棵平衡树维护以它为分治中心时联通块内所有点的ri-di(di为联通块内点到它在点分树上父节点的距离)。

那么显然对于静态的树,单次修改和查询只要从操作点往点分树的根爬并对沿途点进行修改或统计信息即可。

替罪羊式重构

本题的重点是边加点边询问,那么问题来了:如何构建外层点分树?

显然不能加一个点重构一次点分树,而点分树也不具有treap等平衡树的可旋性,但是我们可以像替罪羊树一样重构!

我们知道点分树要求每个点的子树中所有点都是以它为分治中心时能遍历到的点,那么在新加一个点时我们不妨直接将这个点加到它原树的父节点下面。

与替罪羊树相同,我们同样需要一个重构因子,这里因为重构时间复杂度较高,所以重构因子建议设成0.9.

在往根方向修改沿途信息时我们记录距离根最近的一个需要重构的点,然后将这个子树重构。

因为这棵子树在原树上一定是一个联通块,所以直接对这个联通块进行一遍点分治建出这个联通块真正的点分树即可。

因为需要将联通块内所有点的vis标记都清空,所以还需要每个点开一个vector存一下这个点在点分树上的子树中都有哪些点。

当重构外层点分树时,因为点分树上的父子关系改变,所以内层平衡树的信息也要发生相应的变化。

因为外层不是平衡树,我们无法像平衡树一样通过上传来重新改变内层信息,所以只能暴力删除平衡树并暴力重新插入信息。

这也是为什么要用插入时间复杂度较优的平衡树的原因。

LCA

同样因为动态加点,无法用RMQ或者树链剖分等方法求两点在原树的LCA,对于新加入的每个点,只能用倍增来求LCA。

时间复杂度

本题的时间复杂度主要在外层重构及内层平衡树的重建上。

对于外层点分树,重构的时间复杂度每次均摊O(logn)。

对于内层平衡树的重建,因为每个平衡树平均logn个节点,每次插入时间复杂度O(logn),所以重建一棵平衡树时间复杂度O(logn^2)。

一次内层重建logn棵平衡树时间复杂度是O(logn^3)。

同样对于一次查询或修改需要遍历logn个节点,每次求LCA+平衡树上查找时间复杂度O(logn),单次查询或修改时间复杂度O(logn^2)。

注意在点分治递归求重心时每层要dfs一遍整个联通块使当前层的分治中心获得真正的联通块大小,否则会影响重构时的判断。

综上所述,总时间复杂度O(nlogn^3)。

#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
inline char _read()
{static char buf[100000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{int x=0,f=1;char ch=_read();while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=_read();}while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=_read();}return x*f;
}
int n;
ll ans;
int rot;
int num;
int tot;
int cnt;
int top;
int type;
int point;
int x,y,z;
int a,b,c;
int r[100010];
int d[100010];
int f[100010];
int lg[100010];
int to[200010];
int mx[100010];
int dep[100010];
int vis[100010];
int root[100010];
int size[100010];
int head[100010];
int next[200010];
int st[4000010];
int froot[100010];
int g[100010][18];
vector<int>q[100010];
const int mod=1000000000;
struct balance_tree
{int cot;int *flag;int q[4000010];int v[4000010];int ls[4000010];int rs[4000010];int sum[4000010];inline bool bad(int rt){if(sum[rt]*86<=100*max(sum[ls[rt]],sum[rs[rt]])){return true;}return false;}inline void dfs(int rt){if(!rt){return ;}dfs(ls[rt]);q[++cot]=rt;dfs(rs[rt]);}inline void build(int &rt,int l,int r){int mid=(l+r)>>1;rt=q[mid];if(l==r){ls[rt]=rs[rt]=0;sum[rt]=1;return ;}if(l<mid){build(ls[rt],l,mid-1);}else{ls[rt]=0;}build(rs[rt],mid+1,r);sum[rt]=sum[ls[rt]]+sum[rs[rt]]+1;}inline void rebuild(int &rt){cot=0;dfs(rt);if(cot){build(rt,1,cot);}else{rt=0;}}inline void insert(int &rt,int k){if(!rt){if(top){rt=st[top--];}else{rt=++cnt;}v[rt]=k;sum[rt]=1;return ;}sum[rt]++;if(v[rt]>=k){insert(ls[rt],k);}else{insert(rs[rt],k);}if(bad(rt)){flag=&rt;}}inline void ins(int &rt,int val){flag=0;insert(rt,val);if(flag){rebuild(*flag);}}inline void del(int &rt){if(!rt){return ;}del(ls[rt]);del(rs[rt]);v[rt]=sum[rt]=0;st[++top]=rt;rt=0;}inline int query(int root,int k){int rt=root;int ans=0;while(rt){if(v[rt]>=k){rt=ls[rt];}else{ans+=sum[ls[rt]]+1;rt=rs[rt];}}return sum[root]-ans;}
}tr;
inline void add(int x,int y)
{next[++tot]=head[x];head[x]=tot;to[tot]=y;
}
inline int lca(int x,int y)
{if(d[x]<d[y]){swap(x,y);}int deep=d[x]-d[y];for(int i=0;i<=lg[deep];i++){if((deep&(1<<i))){x=g[x][i];}}if(x==y){return x;}for(int i=lg[d[x]];i>=0;i--){if(g[x][i]!=g[y][i]){x=g[x][i];y=g[y][i];}}return g[x][0];
}
inline int dis(int x,int y)
{return dep[x]+dep[y]-(dep[lca(x,y)]<<1);
}
inline void getroot(int x,int fa)
{size[x]=1;mx[x]=0;for(int i=head[x];i;i=next[i]){if(!vis[to[i]]&&to[i]!=fa){getroot(to[i],x);size[x]+=size[to[i]];mx[x]=max(mx[x],size[to[i]]);}}mx[x]=max(mx[x],num-size[x]);if(mx[x]<mx[rot]){rot=x;}
}
inline void dfs(int x,int fa,int rt)
{size[x]=1;q[rt].push_back(x);tr.ins(root[rt],r[x]-dis(x,rt));if(f[rt]){tr.ins(froot[rt],r[x]-dis(x,f[rt]));}for(int i=head[x];i;i=next[i]){if(!vis[to[i]]&&to[i]!=fa){dfs(to[i],x,rt);size[x]+=size[to[i]];}}
}
inline void partation(int x,int fa)
{getroot(x,0);tr.del(root[x]);tr.del(froot[x]);q[x].clear();f[x]=fa;vis[x]=1;dfs(x,0,x);for(int i=head[x];i;i=next[i]){if(!vis[to[i]]){num=size[to[i]];rot=0;getroot(to[i],0);partation(rot,x);}}
}
inline void insert(int x)
{point=-1;for(int i=x;i;i=f[i]){q[i].push_back(x);tr.ins(root[i],r[x]-dis(x,i));if(f[i]){tr.ins(froot[i],r[x]-dis(x,f[i]));}size[i]++;if(f[i]&&size[i]*100>(size[f[i]]+1)*90){point=f[i];}}if(point!=-1){int len=q[point].size();for(int i=0;i<len;i++){vis[q[point][i]]=0;}num=size[point];rot=0;getroot(point,0);partation(rot,f[point]);}
}
inline int query(int x)
{int res=0;for(int i=x;i;i=f[i]){res+=tr.query(root[i],dis(x,i)-r[x]);}for(int i=x;f[i];i=f[i]){res-=tr.query(froot[i],dis(x,f[i])-r[x]);}return res;
}
int main()
{type=read();n=read();x=read();y=read();z=read();printf("0\n");r[1]=z;tr.ins(root[1],r[1]);q[1].push_back(1);mx[0]=1<<30;size[1]=vis[1]=1;for(int i=2;i<=n;i++){lg[i]=lg[i>>1]+1;x=read();y=read();z=read();x^=(ans%mod);r[i]=z;add(i,x);add(x,i);g[i][0]=f[i]=x;d[i]=d[x]+1;dep[i]=dep[x]+y;vis[i]=1;for(int j=1;(1<<j)<=d[i];j++){g[i][j]=g[g[i][j-1]][j-1];}ans+=query(i);printf("%lld\n",ans);insert(i);}
}

转载于:https://www.cnblogs.com/Khada-Jhin/p/10078584.html

BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树)相关推荐

  1. BZOJ 3435: [Wc2014]紫荆花之恋 【(替罪羊树式)动态点分治 + Treap】

    BZOJ 传送门 洛谷传送门 题目分析: 似乎做过几道点分树的题之后题解还是比较好懂的. 这位dalao的题目分析非常的到位,Orz. PoPoQQQ的具体解读也非常的清晰,Orz. R i + R ...

  2. NKOJ 2703 (WC 2014)紫荆花之恋 (点分治+平衡树+替罪羊)

    P2703[WC2014]紫荆花之恋(强数据版) 问题描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花废物的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了 ...

  3. [WC2018]即时战略——动态点分治(替罪羊式点分树)

    题目链接: [WC2018]即时战略 题目大意:给一棵结构未知的树,初始时除1号点其他点都是黑色,1号点是白色,每次你可以询问一条起点为白色终点任意的路径,交互库会自动返回给你这条路径上与起点相邻的节 ...

  4. [Codeforces757G]Can Bash Save the Day?——动态点分治(可持久化点分树)

    题目链接: Codeforces757G 题目大意:给出一棵n个点的树及一个1~n的排列pi,边有边权,有q次操作: 1 l r x 求 $\sum\limits_{i=l}^{r}dis(p_{i} ...

  5. 点分治动态点分治小结

    (写篇博客证明自己还活着×2) 转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/8006488.html 有的时候,我们会发现这样一类题:它长得很像一个$O(n) ...

  6. BZOJ.4553.[HEOI2016TJOI2016]序列(DP 树状数组套线段树/二维线段树(MLE) 动态开点)

    题目链接:BZOJ 洛谷 \(O(n^2)\)DP很好写,对于当前的i从之前满足条件的j中选一个最大值,\(dp[i]=d[j]+1\) for(int j=1; j<i; ++j)if(a[j ...

  7. 【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态点分治

    题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打 ...

  8. BZOJ1095 [ZJOI2007]Hide 捉迷藏 【动态点分治 + 堆】

    题目链接 BZOJ1095 题解 传说中的动态点分治,一直不敢碰 今日一会,感觉其实并不艰涩难懂 考虑没有修改,如果不用树形dp的话,就得点分治 对于每个重心,我们会考虑其分治的子树内所有点到它的距离 ...

  9. bzoj1095: [ZJOI2007]Hide 捉迷藏 动态点分治学习

    好迷啊...感觉动态点分治就是个玄学,蜜汁把树的深度缩到logn (静态)点分治大概是递归的时候分类讨论: 1.答案经过当前点,暴力(雾)算 2.答案不经过当前点,继续递归 由于原树可以长的奇形怪状( ...

最新文章

  1. linux序列比对程序,序列比对软件简单使用教程
  2. 八、Linux 常用 Shell 命令,控制台的快捷键以及 Shell 编程(中)
  3. WordPress主题-Real Estate 7–房地产[更至v3.1.0]
  4. 雅思作文模板.html,雅思小作文常用模板万能句大全
  5. 利用StringUtils工具类进行String为空的判断
  6. 从原理到应用,一文带你了解小程序插件能力
  7. php 去除中英文空格,php 去除所有空格 包括中文空格圆角空格
  8. java怎么获取本机的ip地址_java如何获取本机IP
  9. linux下的etc文件夹
  10. python web微信应用(一) 微信协议分析
  11. OpenResty 在又拍云容器平台中的应用
  12. threejs正方体六面贴图
  13. 计算机网络的时间,计算机网络时间同步技术原理介绍
  14. MySQL 数据类型和约束(外键是重点
  15. 中国十大热门网站榜中榜/Alexa综合排名TOP10(2015)
  16. php开发API接口的代码案例
  17. java new jsonparser_JsonParser is deprecated
  18. 在ABAP的SQL语句中写Oracle Hints
  19. 深度学习理论(李宏毅
  20. 用python画太极

热门文章

  1. python基础 函数 (四)
  2. 安装Wamp后 Apache无法启动的解决方法
  3. 《Programming WPF》翻译 第9章 6.我们进行到哪里了?
  4. ACM 会场安排问题
  5. ZOJ Problem Set - 1067 Color Me Less
  6. cdev 结构体与字符设备的注册
  7. nodejs+express整合kindEditor实现图片上传 - 木子丰咪咕晶 - 开源中国
  8. 可添加至收藏夹并在浏览器地址栏运行的JS代码
  9. Codeforces 803E--Roma and Poker (DP)
  10. 设置UILabel可变高度(根据文本内容自动适应高度)