【WC2014】紫荆花之恋
点分树重构,平衡树
题目大意:
给定一棵树,初始没有结点,每次加入一个结点,带点权 $r_i$ 以及边权。
每次加上一个点后,求满足 $i\lt j$ 且 $\mathrm{dist(i,j)}\leq r_i+r_j$ 的点对 $(i,j)$ 的数量。
强制在线。
题解:
考虑此题的离线做法。
先考虑只询问一次的做法。一种容易想到的做法是对树进行点分治,令 $d_i$ 表示 $i$ 到当前分治中心的距离,则我们要求 $d_i+d_j\leq r_i+r_j$,相当于 $d_i-r_i\leq r_j-d_j$。那么我们对每个结点,可以二分得到它的贡献。
考虑动态加入的情况。我们可以先建出这棵树的点分树,插入的时候对它所在的每个连通块(不超过 $\log n$ 个)都考虑贡献。由于是动态插入的过程,所以我们用平衡树来维护每个连通块的信息。查询时在平衡树上二分即可。注意减去自己子树里的贡献。时间复杂度 $O(n\log^2 n)$,空间复杂度 $O(n\log n)$。
现在加上了强制在线,不能直接建出点分树了。
我们考虑将替罪羊树上使用的重构思想,用在点分树上。即当一个连通块的某个子树大小非常不平衡的时候,对这整个连通块重新做一遍点分治,同时重新得到每个连通块的信息。这样就能够保证均摊复杂度正确了。
由于重构操作比较慢,所以平衡因子可以取得大一点,如 $0.9$。
点分树重构的总复杂度是 $O(n\log n)$ 的,因此最终的复杂度为 $O(n\log^3 n)$。
由于重构操作会对平衡树上的信息产生非常多的修改,所以平衡树部分的效率要高,且需要使用内存回收来防止泄露。
我使用了带旋的 treap 实现,用了很多的 vector,导致效率并不是很高。
Code:
#include<iostream>
#include<random>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+5;
typedef long long LL;
// ------------------------------ Treap begin ------------------------------
mt19937 mt(size_t(new unsigned));
struct node{
unsigned rd;
int ch[2],sz;LL val;
inline void clear(){sz=val=ch[0]=ch[1]=0;}
}d[6000002];
struct MemoryPool{
int sta[6000002],top=0;
MemoryPool(){
for(int i=1;i<=6000000;++i)sta[i]=i,d[i].rd=mt();
top=6000000;
}
inline int new_id(LL v){int x=sta[top--];d[x].sz=1,d[x].val=v;return x;}
inline void recycle(int x){d[x].clear(),sta[++top]=x;}
}P;
inline void update(int x){d[x].sz=d[d[x].ch[0]].sz+d[d[x].ch[1]].sz+1;}
void rotate(int&x,bool c){
int k=d[x].ch[c];
d[x].ch[c]=d[k].ch[!c];
d[k].ch[!c]=x;
update(x),update(x=k);
}
void insert(int&x,int y){
if(!x)x=y;else{
const bool c=d[x].val<=d[y].val;
insert(d[x].ch[c],y);
if(d[x].rd<d[d[x].ch[c]].rd)rotate(x,c);
else update(x);
}
}
int getrk(int u,LL k){
int res=0;
while(u)
if(d[u].val<=k)res+=d[d[u].ch[0]].sz+1,u=d[u].ch[1];
else u=d[u].ch[0];
return res;
}
void dispose(int x){
if(!x)return;
dispose(d[x].ch[0]),dispose(d[x].ch[1]);
P.recycle(x);
}
// ------------------------------ Treap end ------------------------------
LL ans;
struct edge{
int to,nxt,w;
}e[N<<1];
int head[N],cnt,n,V[N];
inline void addedge(int u,int v,int w){
e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
e[++cnt]=(edge){u,head[v],w},head[v]=cnt;
}
struct info{
int id,fa,zx;LL dis;
inline bool operator<(const info&rhs)const{return id<rhs.id;}
};
struct structure{
int _rt,fa;
vector<int>son,_rts;
vector<info>ns;
void reset(){
fa=0,ns.clear();
dispose(_rt);
for(int i:_rts)dispose(i);
_rt=0,_rts.clear();
}
inline int find_id(int x){
return lower_bound(ns.begin(),ns.end(),(info){x})-ns.begin();
}
inline info find_info(int x){
return*lower_bound(ns.begin(),ns.end(),(info){x});
}
}G[N];
bool vis[N];
int root,sz[N],mxd[N],all;
void getrt(int now,int pre){
sz[now]=1,mxd[now]=0;
for(int i=head[now];i;i=e[i].nxt)if(e[i].to!=pre&&vis[e[i].to]){
getrt(e[i].to,now);
sz[now]+=sz[e[i].to];
mxd[now]=max(mxd[now],sz[e[i].to]);
}
mxd[now]=max(mxd[now],all-sz[now]);
if(!root||mxd[now]<mxd[root])root=now;
}
void dfs(int now,int pre,LL d,const int&zx){
G[root].ns.push_back((info){now,pre,zx,d});
insert(G[root]._rts.back(),P.new_id(d-V[now]));
insert(G[root]._rt,P.new_id(d-V[now]));
for(int i=head[now];i;i=e[i].nxt)if(vis[e[i].to]&&e[i].to!=pre)
dfs(e[i].to,now,d+e[i].w,zx);
}
void DFS(int now,int pre,vector<int>&vc){
vc.push_back(now);
for(int i=head[now];i;i=e[i].nxt)if(vis[e[i].to]&&e[i].to!=pre)DFS(e[i].to,now,vc);
}
void rebuild(int x,vector<int>&vc){
for(int i:vc)vis[i]=1;
all=vc.size();
root=0;getrt(vc[0],0);
G[root].fa=x;
G[root].ns.push_back((info){root,0,0,0});
insert(G[root]._rt,P.new_id(-V[root]));
int tt=0,it=0;
for(int i=head[root];i;i=e[i].nxt)if(vis[e[i].to]){
++tt;
G[root]._rts.push_back(0);
dfs(e[i].to,root,e[i].w,(int)G[root]._rts.size()-1);
}
sort(G[root].ns.begin(),G[root].ns.end());
vector<vector<int> >VEC(tt);
for(int i=head[root];i;i=e[i].nxt)if(vis[e[i].to])DFS(e[i].to,root,VEC[it++]);
for(int i:vc)vis[i]=0;
int rt=root;
for(auto&it:VEC)rebuild(rt,it);
}
void insert_(int id,int fa,int vt,int et){
G[id].fa=fa;
G[id]._rt=P.new_id(-vt);
G[id].ns.push_back((info){id,0,0,0});
int re_id=0;
for(int x=fa;x;x=G[x].fa){
const info&F=G[x].find_info(fa);
LL d=F.dis+et;
insert(G[x]._rt,P.new_id(d-vt));
info nw=(info){id,fa,(x==fa?(int)G[x]._rts.size():F.zx),d};
if(x==fa){
G[x]._rts.push_back(P.new_id(d-vt));
}else insert(G[x]._rts[nw.zx],P.new_id(d-vt));
G[x].ns.push_back(nw);
ans+=getrk(G[x]._rt,vt-d)-getrk(G[x]._rts[nw.zx],vt-d);
if(::d[G[x]._rts[nw.zx]].sz*100>=(int)G[x].ns.size()*91)
re_id=x;
}
if(re_id){
vector<int>vc;
int f=G[re_id].fa;
for(info&x:G[re_id].ns)vc.push_back(x.id);
for(int i:vc)G[i].reset();
rebuild(f,vc);
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>n;
int v;
cin>>v>>v>>v;
V[1]=v;
G[1]._rt=P.new_id(-v);
cout<<"0\n";
G[1].ns.push_back((info){1,0,0,0});
for(int i=2;i<=n;++i){
int f,w,t;
cin>>f>>w>>t;
V[i]=t;
f^=ans%1000000000;
addedge(i,f,w);
insert_(i,f,t,w);
cout<<ans<<'\n';
}
return 0;
}