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

时间:2023-01-29 16:53:46

原文链接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)。

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

代码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&num;55 &lbrack;WC2014&rsqb;紫荆花之恋

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

  2. luoguP3920 &lbrack;WC2014&rsqb;紫荆花之恋 动态点分治 &plus; 替罪羊树

    意外的好写..... 考虑点分 \(dis(i, j) \leq r_i + r_j\) 对于过分治中心一点\(u\),有 \(dis(i, u) - r_i = dis(j, u) + r_j\) ...

  3. UOJ &num;55 &amp&semi; 洛谷 P3920 紫荆花之恋 —— 动态点分治&plus;替罪羊树

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

  4. BZOJ3435&lbrack;Wc2014&rsqb;紫荆花之恋——动态点分治&lpar;替罪羊式点分树套替罪羊树&rpar;

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

  5. &lbrack;WC2014&rsqb;紫荆花之恋&lpar;动态点分治&plus;替罪羊思想&rpar;

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

  6. 【BZOJ3435】&lbrack;Wc2014&rsqb;紫荆花之恋 替罪点分树&plus;SBT

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

  7. bzoj 3435&colon; &lbrack;Wc2014&rsqb;紫荆花之恋 替罪羊树维护点分治 &amp&semi;&amp&semi; AC400

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

  8. BZOJ 3435&colon; &lbrack;Wc2014&rsqb;紫荆花之恋

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

  9. 【bzoj3435】&lbrack;Wc2014&rsqb;紫荆花之恋 替罪点分树套SBT

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

随机推荐

  1. C&num;高级知识点01---委托和事件

    委托和事件 什么是委托? 简单来说,就是能把方法当作参数传递的对象,而且还知道怎么去调用这个方法,同时还约束了方法的签名. 例子: 用委托实现插件式编程: 1.

  2. HealthKit开发快速入门教程之HealthKit开发概述简介

    HealthKit开发快速入门教程之HealthKit开发概述简介 2014年6月2日召开的年度开发者大会上,苹果发布了一款新的移动应用平台,可以收集和分析用户的健康数据.该移动应用平台被命名为“He ...

  3. ios-序列帧动画核心代码简单介绍以及封装

    imageView的属性,isAnimating在这里用来当正执行;一个动画的时候,禁止开启其他动画. UIImage imageNamed这个方法加载的图片是有缓存的,它是把所有的图片先加载到内存中 ...

  4. python 安装PyV8 和 lxml

    近来在玩python爬虫,需要使用PyV8模块和lxml模块.但是执行pip install xx 或者easy_install xx 指令都会提示一些错误.这些错误有些是提示pip版本过低或者缺少v ...

  5. Gradle 1&period;12 翻译——第九章 Groovy快速入门

    由于时间关系,没办法同时做笔记和翻译,关于Gradle的用户指南,本博客不再做相关笔记,而只对未翻译章节进行翻译并在此发表. 有关其他已翻译的章节请关注Github上的项目:https://githu ...

  6. Holt-Winters

    https://blog.csdn.net/u010665216/article/details/78051192 mark

  7. 请推荐一本SQL教程

    sql系列教程如下 sql教程 SQL 是用于访问和处理数据库的标准的计算机语言. 在本教程中,您将学到如何使用 SQL 访问和处理数据系统中的数据, 这类数据库包括:mysql.SQL Server ...

  8. 【分片无法挂载】Elasticsearch分片和副本无法挂载&lpar;分片移位&rpar;

    部署说明 硬件 服务器两台: 机器A:64G内存 机器B:32G内存 分片 共12个节点 2个查询节点,10个存储节点 8个主分片 1个复制分片(每个分片都有一个副本分布在不同的节点上面) 每台机器都 ...

  9. 3、iptables扩展及使用

    iptables/netfilter netfilter: kernel framework,位于内核中的协议框架 iptables  是规则管理命令行工具 四表:filter, nat, mangl ...

  10. Asp&period;Net 管道事件注册&sol;HttpApplication事件注册

    一.HttpApplication简介 在HttpRuntime创建了HttpContext对象之后,HttpRuntime将随后创建一个用于处理请求的对象,这个对象的类型为HttpApplicati ...