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

题目描述

强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点。每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/265203.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

捣蛋鹅显示服务器已满,无题大鹅模拟奖杯攻略分享

捣蛋鹅成就怎么解锁&#xff1f;游戏章节不是很长&#xff0c;不同章节中都有不同的奖杯需要解锁&#xff0c;有些比较简单&#xff0c;有的需要一点点技巧&#xff0c;小编这里给大家带来了“PSN lyplyp_lll”总结的无题大鹅模拟奖杯攻略分享&#xff0c;一起来看下文中具体的…

线程的属性 —— 分离的状态(detached state)、栈地址(stack address)、栈大小(stack size)

参考&#xff1a;&#xff08;四十二&#xff09;线程——线程属性 作者&#xff1a;FadeFarAway 发布时间&#xff1a;2017-01-17 14:09:55 网址&#xff1a;https://blog.csdn.net/FadeFarAway/article/details/54576771 目录引入线程属性初始化一、线程的分离状态(detached …

【微信小游戏实战】零基础制作《欢乐停车场》二、关卡设计

1、游戏立项 微信小游戏中有一款《欢乐停车场Plus》的小游戏&#xff0c;大家可以搜索玩下。这是一款益智类的小游戏&#xff0c;游戏中有红、黄、绿、蓝、紫5辆豪车6个停车位&#xff0c;玩家通过可行走路线移动小车&#xff0c;最终让各颜色的小车停到对应的颜色车位&#xf…

服务器端密钥库文件,使用密钥库文件为SOAP运行客户端WS

该错误表示在您的信任库中找不到服务器证书。检查.keystore文件的内容以确定它是否包含服务器证书(在truststore中列为trustedEntry)。如果是&#xff0c;请设置以下系统属性(使用-D JVM参数或System.setProperty())。javax.net.ssl.trustStore<>javax.net.ssl.trustStor…

线程同步(互斥锁、条件、读写锁、信号量)

参考&#xff1a;&#xff08;四十三&#xff09;线程——线程同步&#xff08;互斥锁、读写锁、条件变量、信号量&#xff09; 作者&#xff1a;FadeFarAway 发布时间&#xff1a;2017-01-17 21:25:28 网址&#xff1a;https://blog.csdn.net/FadeFarAway/article/details/545…

Nginx使用Expires增加浏览器缓存加速(转)

转载自&#xff1a;Nginx使用Expires增加浏览器缓存加速 Nginx可以更改HTTP头部&#xff0c;这个是Web服务器必须的&#xff0c;当然Nginx更可以支持在HTTP头部中添加Expires等相关信息&#xff0c;增强浏览器缓存&#xff0c;是网页加载速度增强。 Nginx中使用Expires增强浏览…

Linux curl命令详解

转自&#xff1a;https://www.cnblogs.com/duhuo/p/5695256.html 命令&#xff1a;curl 在Linux中curl是一个利用URL规则在命令行下工作的文件传输工具&#xff0c;可以说是一款很强大的http命令行工具。它支持文件的上传和下载&#xff0c;是综合传输工具&#xff0c;但按传统…

MAC地址、IP地址、ARP协议

B站视频&#xff1a;计算机网络微课堂&#xff08;有字幕无背景音乐版&#xff09; 网址&#xff1a;https://www.bilibili.com/video/BV1c4411d7jb?p61 说明&#xff1a;讲的不错&#xff0c;后期可以继续看此视频学习网络知识 目录MAC地址IP地址ARP协议MAC地址 IP地址 ARP协…

编程学习网站

2019独角兽企业重金招聘Python工程师标准>>> ‍ 刚开始学习编程&#xff0c;如果只是拿着书本拼命敲代码&#xff0c;肯定是非常枯燥的。如果不是非常有决心和毅力可能就会轻易放弃了&#xff0c;有没有学习坡度不那么陡的教程呢&#xff1f; 让我们可以先学点基本的…

C#字符处理

一丶C#删除最后一个字符 例&#xff1a;字符串 string str"1,2,3,4,5,,6,7,8,9," 去掉最后一个逗号 ","; 常用的方法&#xff1a; 1.SubString()方法 strstr.SubString(0,str.Length - 1); 2.Remove()方法 strstr.Remove(str.Length-1,1); 3.TrimEnd()方法…

ARM指针寄存器——堆栈指针寄存器SP、程序计数器PC、连接寄存器LR

参考&#xff1a;堆栈指针寄存器 SP详解以及栈的作用 作者&#xff1a;蓝色鲜橙多 网址&#xff1a;https://blog.csdn.net/qq_36588941/article/details/89873633?utm_sourceapp&app_version4.16.0&codeapp_1562916241&uLinkIdusr1mkqgl919blen 目录堆栈的实现方…

【译】将IDataRecord自动填充到实体的扩展方法

Mapper&#xff1a; Mapper的核心功能是创建一个委托函数并映射到一个目标类的实例。此委托是使用表达式树创建的一个lambda表达式。在这个函数中有一个双重循环&#xff0c;从 DataRecord 获取字段并和从实体类中获取的属性名称比较从而填充实体实例。所以第一个要求就是在使用…

U-Boot 图形化配置及其原理

目录U-Boot 图形化配置体验&#xff08;如何使能dns 命令&#xff09;menuconfig 图形化配置原理make menuconfig 过程分析Kconfig 语法简介1、mainmenu2、调用其他目录下的Kconfig 文件3、menu/endmenu 条目3、config 条目4、depends on 和select4、choice/endchoice5、menuco…

类与对象(三)

定义一个描述教师的类Teacher&#xff0c;数据成员包括工号(Num),姓名(Name、性别(Sex、家庭住址( Addr}、联系电话(Tel}, E-mail地址(Email )、职务(Headship )、职称(Post)和工资(Salary对于数据成员&#xff0c;要求用字符数组实现工号、姓名、家庭住址、联系电话、E-mail地…

OpenStack tokens id获取测试

转载于:https://www.cnblogs.com/heidsoft/p/3524711.html

Linux 内核获取、初次编译、源码目录分析

目录Linux 内核获取Linux 内核初次编译Linux 内核源码目录分析1、arch 目录2、block 目录3、crypto 目录4、Documentation 目录5、drivers 目录6、firmware 目录7、fs 目录8、include 目录9、init 目录10、ipc 目录11、kernel 目录12、lib 目录13、mm 目录Linux 内核获取 关于L…

bzoj 3157 bzoj 3516 国王奇遇记 —— 推式子

题目&#xff1a;https://www.lydsy.com/JudgeOnline/problem.php?id3157 https://www.lydsy.com/JudgeOnline/problem.php?id3516 这篇博客写得太好&#xff1a;http://blog.miskcoo.com/2014/06/bzoj-3157 然而目前之会 \( O(m) \) 的做法&#xff1b; 感觉关键是设计 \( S…

PHP里10个鲜为人知但却非常有用的函数

PHP里有非常丰富的内置函数&#xff0c;很多我们都用过&#xff0c;但仍有很多的函数我们大部分人都不熟悉&#xff0c;可它们却十分的有用。这篇文章里&#xff0c;我列举了一些鲜为人知但会让你眼睛一亮的PHP函数。 levenshtein() 你有没有经历过需要知道两个单词有多大的不同…

前端(jQuery UI)(2)-- jQuery UI interactions

之后补充转载于:https://www.cnblogs.com/foreverlin/p/10127982.html

学会阅读硬件的原理图、数据手册大全

参考&#xff1a; 郭天祥&#xff1a;https://www.bilibili.com/video/BV1DW411a7mz?p8 韦东山&#xff1a;https://www.bilibili.com/video/BV1ga4y1Y7PL?p4 https://www.bilibili.com/video/BV17g411F7oR?spm_id_from333.999.0.0 洋桃电子&#xff1a;https://www.bilibil…