题目描述
强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点。每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 i 都有一个感受能力值Ri ,小精灵 i, j 成为朋友当且仅当在树上 i 和 j 的距离 dist(i,j) ≤ Ri + Rj,其中 dist(i, j)表示在这个树上从 i 到 j 的唯一路径上所有边的边权和。强强和萌萌很好奇每次新长出一个叶子节点之后,这个树上总共有几对朋友。  
我们假定这个树一开始为空,节点按照加入的顺序从 1开始编号。由于强强非常好奇, 你必须在他每次出现新节点后马上给出总共的朋友对数,不能拖延哦。
题解
先考虑给定一棵静态的树,这道题怎么做,显然这是经典的点分治分题,假设当前分治中心为u,对于一个点对ij产生贡献时,当且仅当dis[i]+dis[j]<=r[i]+r[j] -> dis[i]-r[i]<=r[j]-dis[j],我们考虑用平衡树维护所有的dis[i]-r[i]然后用r[i]-dis[i]在平衡树中查有多个数小于等于它就可以了。
那么问题来了,这棵树是动态的,怎么维护。
考虑暴力,每次新建一个点,连一条边,我们可以把原树想象成一颗点分树,每次从新增节点一直往上跳,边跳边更新,这样的话可以过掉小规模的数据和随机生成的数据。
因为这样做树高会被卡成O(n)的,所以不能过。
接下来,我们可以利用替罪羊的思想,每次搞完之后自上而下检查一下,如果发现不平衡,就重构这棵点分树,复杂度同替罪羊树。
实现细节
听起来感觉还可做,实现起来*&¥¥%%¥。
说一下我的具体做法。
在点分树上的每个节点维护这些信息:该点分树内的所有节点到这个点的距离(平衡树),该点分树内所有点到该点在点分树上的父亲的距离(平衡树),该子树所有点的集合(vector),该点到点分树根的一条链(vector)。
每次新增的答案就是当前子树的答案-小子树的答案(其实就是减掉不合法的)。
重构细节很多,注意平衡树的内存回收。
代码
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstdlib>
#define ls tr[p].l
#define rs tr[p].r
#define N 100002
using namespace std;
typedef long long ll;
int topp,q[N*],cnt,head[N],tot,p[N][],dep[N],size[N],siz[N],dp[N],sum,root,rt[N],n,st[N],top,T[N];
ll deep[N],dis[N],r[N],ans;
bool vis[N],tag[N];
vector<int>vec[N],E[N];
struct edge{int n,to;ll l;}e[N<<];
inline void add(int u,int v,ll l){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;}
const double pha=0.75;
const int mod=1e9;
struct node{int size,cnt,l,r,rd;ll val;}tr[N*];
inline void pushup(int p){tr[p].size=tr[ls].size+tr[rs].size+tr[p].cnt;}
inline int newnode(ll x){
int y=topp?q[topp--]:++cnt;
tr[y].l=tr[y].r=;tr[y].val=x;tr[y].rd=rand();tr[y].size=tr[y].cnt=;
return y;
}
inline void re(int x){q[++topp]=x;}
inline void lturn(int &p){
int x=tr[p].r;tr[p].r=tr[x].l;tr[x].l=p;
tr[x].size=tr[p].size;pushup(p);p=x;
}
inline void rturn(int &p){
int x=tr[p].l;tr[p].l=tr[x].r;tr[x].r=p;
tr[x].size=tr[p].size;pushup(p);p=x;
}
inline void ins(int &p,ll x){
if(!p){p=newnode(x);return;}
tr[p].size++;
if(tr[p].val==x){tr[p].cnt++;return;}
if(x>tr[p].val){
ins(rs,x);
if(tr[p].rd>tr[rs].rd)lturn(p);
}
else{
ins(ls,x);
if(tr[p].rd>tr[ls].rd)rturn(p);
}
}
inline ll get_rank(int p,ll x){
if(!p)return ;
if(tr[p].val<=x)return tr[ls].size+tr[p].cnt+get_rank(rs,x);
return get_rank(ls,x);
}
void bl(int p){if(ls)bl(ls);printf("%lld ",tr[p].val);if(rs)bl(rs);}
inline ll rd(){
int x=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);c=getchar();}
return f?-x:x;
}
inline int getlca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
for(int i=;i>=;--i)if(dep[a]-(<<i)>=dep[b])a=p[a][i];
if(a==b)return a;
for(int i=;i>=;--i)if(p[a][i]!=p[b][i])a=p[a][i],b=p[b][i];
return p[a][];
}
inline ll dist(int a,int b){return dis[a]+dis[b]-*dis[getlca(a,b)];}
void getsize(int u,int fa){
siz[u]=;
for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa&&vis[e[i].to]&&!tag[e[i].to]){
int v=e[i].to;getsize(v,u);siz[u]+=siz[v];
}
}
void getroot(int u,int fa){
dp[u]=;siz[u]=;
for(int i=head[u];i;i=e[i].n)if(vis[e[i].to]&&e[i].to!=fa&&!tag[e[i].to]){
int v=e[i].to;getroot(v,u);
siz[u]+=siz[v];dp[u]=max(dp[u],siz[v]);
}
dp[u]=max(sum-siz[u],dp[u]);
if(dp[u]<dp[root])root=u;
}
void getdeep(int u,int fa,int top,int tt){
ins(rt[top],deep[u]-r[u]);E[top].push_back(u);
ins(T[tt],deep[u]-r[u]);vec[u].push_back(top);
size[top]++;
for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa&&vis[e[i].to]&&!tag[e[i].to]){
int v=e[i].to;deep[v]=deep[u]+e[i].l;
getdeep(v,u,top,tt);
}
}
void solve(int u){
tag[u]=;
size[u]=;E[u].push_back(u);
ins(rt[u],-r[u]);
for(int i=head[u];i;i=e[i].n)if(vis[e[i].to]&&!tag[e[i].to]){
int v=e[i].to;
sum=siz[v];root=n+;
getroot(v,u);getsize(root,);
deep[v]=e[i].l;getdeep(v,u,u,root);
solve(root);
}
}
void efs(int p){if(!p)return;if(ls)efs(ls);if(rs)efs(rs);re(p);}
inline void rebuild(int u){
int ma=E[u].size();
for(int i=;i<ma;++i){
int v=E[u][i];
st[++top]=v;vis[v]=;
}
ma=vec[u].size();
int king=ma?vec[u].back():;
for(int i=;i<=top;++i){
int x=st[i];size[x]=;
E[x].clear();
while(vec[x].size()&&vec[x].back()!=king)vec[x].pop_back();
efs(rt[x]);efs(T[x]);rt[x]=T[x]=;
}
root=n+;sum=top;dp[root]=n+;
getroot(u,);getsize(root,);int haha=root;
solve(root);
if(king){
for(int i=;i<=top;++i){
int x=st[i];
ll d=dist(x,king);
ins(T[haha],d-r[x]);
}
}
while(top){
int x=st[top];
vis[st[top]]=;
tag[st[top]]=;
top--;
}
}
inline void check(int u){
int ma=vec[u].size();
for(int i=;i<ma;++i){
int v=vec[u][i],v1=vec[u][i-];
if(size[v1]*pha<size[v]){
rebuild(v1);
break;
}
}
}
inline void work(int u,int fa){
int ma=vec[fa].size();
for(int i=;i<ma;++i)vec[u].push_back(vec[fa][i]);
vec[u].push_back(fa);
ins(rt[u],-r[u]);size[u]=;E[u].push_back(u);
ma++;
for(int i=ma-;i>=;--i){
int v=vec[u][i];ll d=dist(u,v);
size[v]++;E[v].push_back(u);
int nex=i==ma-?u:vec[u][i+];
ins(rt[v],d-r[u]);ins(T[nex],d-r[u]);
ans+=get_rank(rt[v],r[u]-d)-get_rank(T[nex],r[u]-d);
}
check(u);
}
int main(){
srand();
n=rd();n=rd();
ll a=rd(),c=rd();r[]=rd();
size[]=;ins(rt[],-r[]);
E[].push_back();
printf("%lld\n",ans);
for(int i=;i<=n;++i){
a=rd();c=rd();r[i]=rd();
a=a^(ans%mod);
add(i,a,c);add(a,i,c);
dep[i]=dep[a]+;dis[i]=dis[a]+c;p[i][]=a;
for(int j=;(<<j)<=dep[i];++j)p[i][j]=p[p[i][j-]][j-];
work(i,a);
printf("%lld\n",ans);
}
return ;
}  
 

[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. uoj 55 紫荆花之恋 动态点分治+替罪羊式重构+treap

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

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

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

  5. bzoj 3435: [Wc2014]紫荆花之恋 替罪羊树维护点分治 && AC400

    3435: [Wc2014]紫荆花之恋 Time Limit: 240 Sec  Memory Limit: 512 MBSubmit: 159  Solved: 40[Submit][Status] ...

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

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

  7. BZOJ 3435: [Wc2014]紫荆花之恋

    二次联通门 : BZOJ 3435: [Wc2014]紫荆花之恋 二次联通门 : luogu P3920 [WC2014]紫荆花之恋 /* luogu P3920 [WC2014]紫荆花之恋 怀疑人生 ...

  8. BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋 (替罪羊树 动态点分治 套 Treap)

    题意 略 分析 引用PoPoQQQ的话 吾辈有生之年终于把这道题切了...QAQ (蒟蒻狂笑) Orz PoPoQQQ,我又抄PoPoQQQ的题解了 - 突然发现有旋Treap没那么难写 学习了一波C ...

  9. UOJ#55. 【WC2014】紫荆花之恋 点分树 替罪羊树 平衡树 splay Treap

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ55.html 题解 做法还是挺容易想到的. 但是写的话…… 首先这种题如果只要求一棵树中的满足条件的点数( ...

随机推荐

  1. Requires: libc.so.6(GLIBC_2.14)(64bit)

    centos6 - CentOS 6 - libc.so.6(GLIBC_2.14)(64bit) is needed by - Server Faulthttps://serverfault.com ...

  2. nginx 1.4.3能直接升到1.8.1吗

    nginx 1.4.3能直接升到1.8.1吗_百度知道https://zhidao.baidu.com/question/564529441847261484.html nginx-1.6.3平滑升级 ...

  3. centos 检测aufs 并安装

    http://www.cnblogs.com/logo-fox/p/7366506.html 因为DeviceMapper不稳定,所以必须升级到3.10以上的内核,运行docker(2.6提示运行do ...

  4. sqlServer问题记录

    1.sql 2008 无法绑定由多个部分绑定的标示符 连接中的多个表中存在同名字段,通过设置别名访问即可 2.远程无法连接到sqlserver 计算机管理->服务与应用程序->SQL Se ...

  5. css行内省略号、垂直居中

    应用场景分析: 一.当你的文字限定行数,超出部分的文字用省略号显示. (有两个使用场景:1.单行 2.多行) // 单行 overflow: hidden; text-overflow:ellipsi ...

  6. java 代理模式(静态代理、动态代理、Cglib代理) 转载

    Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...

  7. Oracle数值函数

    --数值函数 --四舍五入 ) from dual ) from dual --数字截取 ) from dual --取模 ,) from dual

  8. Golang的interface实践

    这是第二个我在别的语言里面没有见过的实现,go的interface可以说是独树一帜,让我们仔细来实践一下. interface类型是什么?interface类型定义了一组方法,如果某个对象实现了某个接 ...

  9. C# 中那些常用的工具类(Utility Class)(二)

    今天按照这一年来经常用到的那些静态的工具类再来做一次总结,这些小的工具来可以作为自己学习的很好的例子,通过总结这些东西,能够很大程度上梳理自己的知识体系,当然这个是经常用到的,接下来就一个个去分析这些 ...

  10. 简单谈谈数据库DML、DDL和DCL的区别

    一.DML DML(data manipulation language)数据操纵语言: 就是我们最经常用到的 SELECT.UPDATE.INSERT.DELETE. 主要用来对数据库的数据进行一些 ...