原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ55.html

题解

做法还是挺容易想到的。

但是写的话……

首先这种题如果只要求一棵树中的满足条件的点数(不需要在加点的同时维护答案),那么显然可以点分治:

假设当前点分中心为 x,设点 y 与 x 的距离为 d[y] ,然后,我们把 $d[a] + d[b] \leq r[a] + r[b]$ 移一下项,得到:

$$d[a]-r[a]\leq r[b] - d[b]$$

那么,我们只需要对于每一个点 b 求出与它不在 x 的同一个子树的节点 a 的个数,a 要满足 $d[a]-r[a]\leq r[b]-d[b]$ 。这个东西显然是可以用数据结构维护的。

具体用什么呢?

再说。

现在我们要动态维护答案。考虑如果可以离线怎么做:

对最终的树建一个点分树,也就是搞一个动态点分治。对于每一个点分中心,维护他控制的区域内所有节点的 d[a]-r[a] ;对于他的每一个子树,也维护一下这个东西。

那么加入一个点的时候,我们只要在点分树上走一趟,通过维护的信息更新答案的同时加入当前节点的贡献。

所以我们要的数据结构得支持插入和询问排名,那么自然选用平衡树。

所以离线的复杂度是 $O(n\log^2 n)$ 。

然而题目要求强制在线。

怎么办?

定期重构点分树?复杂度好像是 $O(n\sqrt n \log  n)$ 。好像和暴力得分一样……

定期重构太naive了,我们给他升个级!

替罪羊树:当点分树的一个子树划分的过度不均匀的时候,重构子树。

恭喜你得到了一个 $O(n\log^3 n)$ 的算法。

接下来是写代码和调代码的欢♂乐时光。

可怕的是,这题还卡常数……

下面给出两份代码,一个是splay(68331ms的那个),一个是非旋Treap(107878ms)。

代码1 - splay

#pragma GCC optimize("Ofast","inline")
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=100005,INF=1.05e9,mod=1e9;
const double checkval=0.80;
namespace spl{
const int S=N*100;
int son[S][2],fa[S],val[S],size[S];
int cnt=0;
namespace cly{
int st[S],top=0;
inline int new_node(){
return top?st[top--]:++cnt;
}
inline void Recover(int x){
st[++top]=x;
son[x][0]=son[x][1]=fa[x]=val[x]=size[x]=0;
}
}
using cly::new_node;
using cly::Recover;
#define ls son[x][0]
#define rs son[x][1]
int new_node(int v){
int x=new_node();
val[x]=v;
return x;
}
inline void pushup(int x){
size[x]=size[ls]+size[rs]+1;
}
inline int wson(int x){
return son[fa[x]][1]==x;
}
void rotate(int x){
int y=fa[x],z=fa[y],L=son[y][1]==x,R=L^1;
if (z)
son[z][son[z][1]==y]=x;
fa[x]=z,fa[y]=x,fa[son[x][R]]=y;
son[y][L]=son[x][R],son[x][R]=y;
pushup(y),pushup(x);
}
void splay(int x){
for (int y=fa[x];fa[x];rotate(x),y=fa[x])
if (fa[y])
rotate(wson(x)==wson(y)?y:x);
}
inline void Ins(int &a,int v){
register int x=a,c=0;
int f=0;
while (x){
c++;
size[x]++;
f=x;
x=v<val[x]?ls:rs;
}
val[son[f][v>=val[f]]=x=new_node()]=v,fa[x]=f,size[x]=1;
if (!a||c>30)
splay(a=x);
}
int Query(int &rt,int v){
int ans=0,c=0;
register int x=rt,p=0;
while (x){
p=x,c++;
if (val[x]<=v)
ans+=size[ls]+1,x=rs;
else
x=ls;
}
if (p&&c>30)
splay(rt=p);
return ans;
}
void Remove(int x){
if (x)
Remove(ls),Remove(rs),Recover(x);
}
int id[N];
void Build(int &x,int L,int R,int f){
if (L>R)
return;
int mid=(L+R)>>1;
fa[x=id[mid]]=f;
Build(ls,L,mid-1,x);
Build(rs,mid+1,R,x);
pushup(x);
}
void Build(int &x,vector <int> &v){
if (v.empty())
return;
int n=0;
sort(v.begin(),v.end());
for (auto i : v)
id[++n]=new_node(i);
Build(x,1,n,0);
}
#undef ls
#undef rs
}
int Test,n;
LL ans=0;
vector <int> e[N];
int fa[N][20],depth[N],len[N];
int LCA(int x,int y){
if (depth[x]<depth[y])
swap(x,y);
Fod(i,16,0)
if (depth[x]-(1<<i)>=depth[y])
x=fa[x][i];
if (x==y)
return x;
Fod(i,16,0)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int Dis(int x,int y){
return len[x]+len[y]-2*len[LCA(x,y)];
}
int r[N];
namespace dt{
int fa[N],size[N],mxs[N],depth[N];
int rt[N],rtf[N];
int node_cnt=0;
void Init(){
clr(fa),clr(size),clr(mxs),clr(depth),clr(rt),clr(rtf);
depth[0]=-1,node_cnt++;
size[1]=1,mxs[1]=depth[1]=rt[1]=0;
spl::Ins(rt[1],0-r[1]);
}
int id[N],idc=0;
void dfs(int x,int pre,int d){
id[++idc]=x;
for (auto y : e[x])
if (y!=pre&&depth[y]>=d)
dfs(y,x,d);
}
int sz[N],msz[N],RT,Size;
void dfs2(int x,int pre){
sz[x]=1,msz[x]=0;
for (auto y : e[x])
if (y!=pre&&!size[y]){
dfs2(y,x);
sz[x]+=sz[y];
msz[x]=max(msz[x],sz[y]);
}
msz[x]=max(msz[x],Size-sz[x]);
if (!RT||msz[x]<msz[RT])
RT=x;
}
vector <int> id1,id2;
void dfs3(int x,int pre,int anc){
sz[x]=1;
id1.pb(Dis(x,anc)-r[x]);
if (fa[anc])
id2.pb(Dis(x,fa[anc])-r[x]);
for (auto y : e[x])
if (y!=pre&&!size[y])
dfs3(y,x,anc),sz[x]+=sz[y];
}
void build(int x,int f,int n){
RT=0,Size=n;
dfs2(x,0);
x=RT;
fa[x]=f,depth[x]=depth[f]+1,size[x]=n,mxs[x]=msz[x];
id1.clear(),id2.clear();
dfs3(x,0,x);
spl::Build(rt[x],id1);
spl::Build(rtf[x],id2);
for (auto y : e[x])
if (!size[y])
build(y,x,sz[y]);
}
void Rebuild(int x,int f){
idc=0;
dfs(x,0,depth[x]);
For(_t,1,idc){
int i=id[_t];
spl::Remove(rt[i]),spl::Remove(rtf[i]);
depth[i]=size[i]=fa[i]=mxs[i]=rt[i]=rtf[i]=0;
}
build(x,f,idc);
}
void Ins(int x,int f){
static vector <int> v;
fa[x]=f,depth[x]=depth[f]+1;
rt[x]=rtf[x]=0;
v.clear();
for (int i=x;i;i=fa[i]){
v.pb(i),size[i]++;
mxs[fa[i]]=max(mxs[fa[i]],size[i]);
spl::Ins(rt[i],Dis(i,x)-r[x]);
if (fa[i])
spl::Ins(rtf[i],Dis(fa[i],x)-r[x]);
}
node_cnt++;
reverse(v.begin(),v.end());
for (auto i : v)
if (mxs[i]>checkval*size[i]){
Rebuild(i,fa[i]);
break;
}
if (size[x]>1)
ans+=spl::Query(rt[x],r[x])-1;
for (int i=x,f,d;depth[i];i=f){
f=fa[i],d=Dis(f,x);
ans+=spl::Query(rt[f],r[x]-d)-spl::Query(rtf[i],r[x]-d);
}
}
}
int main(){
Test=read(),n=read();
For(i,1,n){
int f=read()^(ans%mod),c=read();
r[i]=read();
if (!f){
dt::Init();
printf("%lld\n",ans);
continue;
}
assert(1<=f&&f<i);
e[i].pb(f),e[f].pb(i);
fa[i][0]=f;
For(j,1,16)
fa[i][j]=fa[fa[i][j-1]][j-1];
depth[i]=depth[f]+1;
len[i]=len[f]+c;
dt::Ins(i,f);
printf("%lld\n",ans);
}
return 0;
}

  

代码2 - Treap(非旋)

#pragma GCC optimize("Ofast","inline")
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=100005,INF=1.05e9,mod=1e9;
const double checkval=0.80;
int randint(){
#ifdef windows
return (rand()<<15)^rand();
#else
return rand();
#endif
}
namespace Treap{
const int S=N*100;
int son[S][2],val[S],ckv[S],size[S];
int cnt=0;
namespace cly{
int st[S],top=0;
inline int new_node(){
return top?st[top--]:++cnt;
}
inline void Recover(int x){
st[++top]=x,son[x][0]=son[x][1]=val[x]=size[x]=0;
}
}
#define ls son[x][0]
#define rs son[x][1]
int new_node(int v){
int x=cly::new_node();
val[x]=v,ckv[x]=randint(),size[x]=1;
return x;
}
void pushup(int x){
size[x]=size[ls]+size[rs]+1;
}
int Merge(int x,int y){
if (!x||!y)
return x+y;
if (ckv[x]<ckv[y]){
son[x][1]=Merge(son[x][1],y),pushup(x);
return x;
}
else {
son[y][0]=Merge(x,son[y][0]),pushup(y);
return y;
}
}
pair <int,int> Split(int x,int k){
if (!x)
return mp(0,0);
if (k<=size[ls]){
pair <int,int> p=Split(ls,k);
ls=p.se,pushup(x);
return mp(p.fi,x);
}
else {
pair <int,int> p=Split(rs,k-size[ls]-1);
rs=p.fi,pushup(x);
return mp(x,p.se);
}
}
int _Rank(int x,int v){
return x?(val[x]>v?_Rank(ls,v):size[ls]+1+_Rank(rs,v)):0;
}
int Rank(int x,int v){
return _Rank(x,v-1);
}
int Query(int x,int v){
return _Rank(x,v);
}
int id[N];
int tL[N],tR[N],lcnt,rcnt;
void Build(int &x,int L,int R){
if (L>R)
return;
int Mi=L;
For(i,L+1,R)
if (ckv[id[i]]<ckv[id[Mi]])
Mi=i;
swap(id[Mi],id[R]);
x=id[R];
lcnt=rcnt=0;
For(i,L,R-1)
if (val[id[i]]<=val[x])
tL[++lcnt]=id[i];
else
tR[++rcnt]=id[i];
int c=L-1;
For(i,1,lcnt)
id[++c]=tL[i];
int mid=c;
For(i,1,rcnt)
id[++c]=tR[i];
Build(ls,L,mid);
Build(rs,mid+1,R-1);
pushup(x);
}
void Build(int &x,vector <int> &v){
if (v.empty())
return;
int n=0;
for (auto i : v)
id[++n]=new_node(i);
Build(x,1,n);
}
void Ins(int &x,int v){
pair <int,int> p=Split(x,_Rank(x,v));
x=Merge(p.fi,Merge(new_node(v),p.se));
}
void Remove(int x){
if (x)
Remove(ls),Remove(rs),cly::Recover(x);
}
#undef ls
#undef rs
}
int Test,n;
LL ans=0;
vector <int> e[N];
int fa[N][20],depth[N],len[N];
int LCA(int x,int y){
if (depth[x]<depth[y])
swap(x,y);
Fod(i,16,0)
if (depth[x]-(1<<i)>=depth[y])
x=fa[x][i];
if (x==y)
return x;
Fod(i,16,0)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int Dis(int x,int y){
return len[x]+len[y]-2*len[LCA(x,y)];
}
int r[N];
namespace dt{
int fa[N],size[N],mxs[N],depth[N];
int rt[N],rtf[N];
int node_cnt=0;
void Init(){
clr(fa),clr(size),clr(mxs),clr(depth),clr(rt),clr(rtf);
depth[0]=-1,node_cnt++;
size[1]=1,mxs[1]=depth[1]=rt[1]=0;
Treap::Ins(rt[1],0-r[1]);
}
int id[N],idc=0;
void dfs(int x,int pre,int d){
id[++idc]=x;
for (auto y : e[x])
if (y!=pre&&depth[y]>=d)
dfs(y,x,d);
}
int sz[N],msz[N],RT,Size;
void dfs2(int x,int pre){
sz[x]=1,msz[x]=0;
for (auto y : e[x])
if (y!=pre&&!size[y]){
dfs2(y,x);
sz[x]+=sz[y];
msz[x]=max(msz[x],sz[y]);
}
msz[x]=max(msz[x],Size-sz[x]);
if (!RT||msz[x]<msz[RT])
RT=x;
}
vector <int> id1,id2;
void dfs3(int x,int pre,int anc){
sz[x]=1;
id1.pb(Dis(x,anc)-r[x]);
if (fa[anc])
id2.pb(Dis(x,fa[anc])-r[x]);
for (auto y : e[x])
if (y!=pre&&!size[y])
dfs3(y,x,anc),sz[x]+=sz[y];
}
void build(int x,int f,int n){
RT=0,Size=n;
dfs2(x,0);
x=RT;
fa[x]=f,depth[x]=depth[f]+1,size[x]=n,mxs[x]=msz[x];
id1.clear(),id2.clear();
dfs3(x,0,x);
Treap::Build(rt[x],id1);
Treap::Build(rtf[x],id2);
for (auto y : e[x])
if (!size[y])
build(y,x,sz[y]);
}
void Rebuild(int x,int f){
idc=0;
dfs(x,0,depth[x]);
For(_t,1,idc){
int i=id[_t];
Treap::Remove(rt[i]),Treap::Remove(rtf[i]);
depth[i]=size[i]=fa[i]=mxs[i]=rt[i]=rtf[i]=0;
}
build(x,f,idc);
}
void Ins(int x,int f){
static vector <int> v;
fa[x]=f,depth[x]=depth[f]+1;
rt[x]=rtf[x]=0;
v.clear();
for (int i=x;i;i=fa[i]){
v.pb(i),size[i]++;
mxs[fa[i]]=max(mxs[fa[i]],size[i]);
Treap::Ins(rt[i],Dis(i,x)-r[x]);
if (fa[i])
Treap::Ins(rtf[i],Dis(fa[i],x)-r[x]);
}
node_cnt++;
reverse(v.begin(),v.end());
for (auto i : v)
if (mxs[i]>checkval*size[i]){
Rebuild(i,fa[i]);
break;
}
if (size[x]>1)
ans+=Treap::Query(rt[x],r[x])-1;
for (int i=x,f,d;depth[i];i=f){
f=fa[i],d=Dis(f,x);
ans+=Treap::Query(rt[f],r[x]-d)-Treap::Query(rtf[i],r[x]-d);
}
}
}
int main(){
srand(_SEED_);
Test=read(),n=read();
For(i,1,n){
int f=read()^(ans%mod),c=read();
r[i]=read();
if (!f){
dt::Init();
printf("%lld\n",ans);
continue;
}
e[i].pb(f),e[f].pb(i);
fa[i][0]=f;
For(j,1,16)
fa[i][j]=fa[fa[i][j-1]][j-1];
depth[i]=depth[f]+1;
len[i]=len[f]+c;
dt::Ins(i,f);
printf("%lld\n",ans);
}
return 0;
}

  

UOJ#55. 【WC2014】紫荆花之恋 点分树 替罪羊树 平衡树 splay Treap的更多相关文章

  1. UOJ#55 [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 & 洛谷 P3920 紫荆花之恋 —— 动态点分治+替罪羊树

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. Android学习第十天

    计算机表示图形的几种方式 a)         Bmp:以高质量保存,用于计算机 b)         Jpg:以良好的质量保存,用于计算机或网络 c)         Png:以高质量保存 d)   ...

  2. P4610 [COCI2011-2012#7] KAMPANJA

    题目背景 临近选举,总统要在城市1和城市2举行演讲.他乘汽车完成巡回演讲,从1出发,途中要经过城市2,最后必须回到城市1.特勤局对总统要经过的所有城市监控.为了使得费用最小,必须使得监控的城市最少.求 ...

  3. Python网络编程(3)——SocketServer模块与简单并发服务器

    主要类型 该模块有四个比较主要的类,其中常用的是 TCPServer 和 UDPServer. 1. TCPServer 2. UDPServer 3. UnixStreamServer,类似于TCP ...

  4. BZOJ2870 最长道路

    题意:给定树,有点权.求一条路径使得最小点权 * 总点数最大.只需输出这个最大值.5w. 解:树上路径问题,点分治. 考虑合并两个子树的时候,答案的形式是val1 * (d1 + d2),当1是新插入 ...

  5. mongodb中比较级查询条件:($lt $lte $gt $gte)(大于、小于)、查找条件

    查询表中学生年级大于20,如下: db.getCollection('student').find({'age':{'$gt':'20'}}) $lt    <   (less  than ) ...

  6. Springboot 4.Springboot 集成SwaggerUi

    SwaggerUi就是自动生成接口文档的这么一个类似于插件的工具,可以直接访问接口. 首先打开pom文件,将插件引进来,然后增加一个属性<properties>,用来设置版本号的,然后直接 ...

  7. 金融量化分析【day112】:均值回归策略

    一.均值回归策略 1.什么是回归策略 二.归一标准化 import numpy as np a = np.random.uniform(100,5000,1000) b = np.random.uni ...

  8. [JVM-4]Java内存模型(JMM)

    Java 内存模型 屏蔽掉各种硬件和操作系统的内存访问差异. 1 主内存和工作内存之间的交互 2 对于 volatile 型变量的特殊规则 关键字 volatile 是 Java 虚拟机提供的最轻量级 ...

  9. Spring的事务机制

    ---恢复内容开始--- 内定的=>(只需要在xml 中添加一个bean) 在xml 中添加 <bean id="listener" class="com.t ...

  10. Geometric regularity criterion for NSE: the cross product of velocity and vorticity 4: $u\cdot \om$

    在 [Berselli, Luigi C.; Córdoba, Diego. On the regularity of the solutions to the 3D Navier-Stokes eq ...