传送门
题意:有一棵开始时没有结点的树,nnn次询问,每次新加一点并给定父结点、到父亲的距离、参数rir_iri,并询问满足dist(u,v)≤ru+rvdist(u,v)\leq r_u+r_vdist(u,v)≤ru+rv的点对(u,v)(u,v)(u,v)的对数。
n≤105n\leq 10^5n≤105,强制在线
很好的一道题,并没有江湖上传言的那么恐怖我只写了两天而已
显然我们只需要考虑新加的点的贡献和之前加起来即可
设当前加的点为uuu
盯着这个式子:
dist(u,v)≤ru+rvdist(u,v)\leq r_u+r_vdist(u,v)≤ru+rv
如果我们随便找到uuu到vvv路径上的一点ppp ,可以拆成
dist(u,p)+dist(v,p)≤ru+rvdist(u,p)+dist(v,p)\leq r_u+r_vdist(u,p)+dist(v,p)≤ru+rv
我们要求的就是满足
dist(v,p)−rv≤ru−dist(u,p)dist(v,p)-r_v\leq r_u-dist(u,p)dist(v,p)−rv≤ru−dist(u,p)
的vvv的个数
如果ppp固定,就可以用平衡树瞎维护一下
然后可以枚举uuu的祖先作为ppp统计答案
但显然给条链就挂了
注意到我们这样做的复杂度是O(u的深度)O(u的深度)O(u的深度),可以试试点分树
如果树的形态是固定的,因为点分树的性质
点分树上两点的lcalcalca在原树上这两点的路径上
并且我们上面要求的恰好只是在路径上,所以可以在点分树上用相同的方式统计
具体实现的时候,每个点分树上的结点uuu开棵平衡树记录子树中所有点vvv的dist(v,u)−rvdist(v,u)-r_vdist(v,u)−rv
然后设新加的点为xxx,从xxx开始往上跳,跳到uuu时询问uuu的平衡树中≤rx−dist(x,u)\leq r_x-dist(x,u)≤rx−dist(x,u)的点的个数
但这样会算进不是简单路径的,所以需要记录uuu的 某个儿子 的子树 中的结点vvv的dist(v,u)−rvdist(v,u)-r_vdist(v,u)−rv的信息减一下
具体而言就是每个结点uuu再开一个平衡树记录子树中的每个结点vvv的dist(v,fau)−rvdist(v,fa_u)-r_vdist(v,fau)−rv
但是这棵树还会动
采用黑科技,用替罪羊树的思想,在跳点分树的时候如果有偏得太严重的点,就把整个子树重构
说着简单其实细节不少
因为常数大得惊人,所以内部平衡树用了替罪羊树
真·奥义·替罪羊套替罪羊
复杂度O(能过)O(能过)O(能过)
本题的易错细节:
- 内部替罪羊重构的时候要清空chchch数组
- 点分树重构子树的时候在原树上只是一个连通块,需要dfs一遍打个标记,后面只访问有标记的点
- 点分树重构子树时要清空所有结点的一堆信息
- 因为要算distdistdist,在点分树重构前要先把新的子树的根(即整个连通块的重心)的父亲接上
- 要记录点分树上每个点是父亲的第几个儿子并传引用
- 在点分树重构时暴力建平衡树要开两个vector并分别排序
- 点分树重构不能修改up,disup,disup,dis等信息
- 判断重构时要特判是整个点分树的根的情况
- 要写内存回收池
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <algorithm>
#include <cassert>
#define MAXN 100005
#define MAXM 200005
using namespace std;
const int MOD=1e9;
const double alpha=0.8;
typedef long long ll;
struct edge{int u,v,w;}e[MAXM];
int head[MAXN],nxt[MAXM],cnt;
inline void addnode(int u,int v,int w)
{e[++cnt]=(edge){u,v,w};nxt[cnt]=head[u];head[u]=cnt;
}
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
int dis[MAXN],dep[MAXN],up[MAXN][20];
inline int lca(int x,int y)
{if (dep[x]<dep[y]) swap(x,y);int t=dep[x]-dep[y];for (int i=0;(1<<i)<=t;i++) if (t&(1<<i)) x=up[x][i];if (x==y) return x;for (int i=19;i>=0;i--) if (up[x][i]!=up[y][i]) x=up[x][i],y=up[y][i];return up[x][0];
}
inline int dist(const int& x,const int& y){return dis[x]+dis[y]-2*dis[lca(x,y)];}
namespace SGT
{int *pos;const int N=MAXN<<6;int bin[N],lis[N];int ch[N][2],siz[N],val[N],tot;inline int newnode(const int& v){int x=(bin[0]? bin[bin[0]--]:++tot);return ch[x][0]=ch[x][1]=0,siz[x]=1,val[x]=v,x;}inline void update(const int& x){siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]];}void insert(int& x,int v){if (!x) return (void)(x=newnode(v));insert(ch[x][v>val[x]],v);update(x);if (siz[ch[x][0]]>siz[x]*alpha||siz[ch[x][1]]>siz[x]*alpha) pos=&x;}void build(int& x,int l=1,int r=lis[0]){if (l>r) return (void)(x=0);int mid=(l+r)>>1;x=newnode(lis[mid]),build(ch[x][0],l,mid-1),build(ch[x][1],mid+1,r),update(x);}void dfs(int& x){if (!x) return;dfs(ch[x][0]);bin[++bin[0]]=x,lis[++lis[0]]=val[x];dfs(ch[x][1]);x=0;}inline void rebuild(){if (pos==NULL) return;lis[0]=0,dfs(*pos),build(*pos),pos=NULL;}inline void modify(int& x,int v){insert(x,v),rebuild();}int query(int x,int v){if (!x) return 0;if (v<val[x]) return query(ch[x][0],v);return siz[ch[x][0]]+1+query(ch[x][1],v);}inline void getbuf(const vector<int>& v){lis[0]=0;for (int i=0;i<(int)v.size();i++) lis[++lis[0]]=v[i];}
}
using SGT::modify;
using SGT::query;
using SGT::getbuf;
int fa[MAXN],idx[MAXN],rt[MAXN],prt[MAXN],r[MAXN],*pos;
int Rt=1;
vector<int> son[MAXN],sub[MAXN];
inline void addnode(int u,int f){fa[u]=f,son[f].push_back(u),idx[u]=son[f].size()-1;}
inline int insert(int x)
{int ans=0;for (int u=x,v=0;u;v=u,u=fa[u]){int val=r[x]-dist(x,u);ans+=query(rt[u],val)-query(prt[v],val);modify(rt[u],-val);if (v) modify(prt[v],-val);if (SGT::siz[rt[v]]>SGT::siz[rt[u]]*alpha) pos=(fa[u]? &son[fa[u]][idx[u]]:&Rt);}return ans;
}
bool vis[MAXN];
vector<int> block;
void dfs(int u){vis[u]=1,block.push_back(u);for (int i=0;i<(int)son[u].size();i++) dfs(son[u][i]);}
int siz[MAXN],maxp[MAXN]={0x7fffffff},root;
void findrt(int u,int f,int sum)
{siz[u]=1,maxp[u]=0;for (int i=head[u];i;i=nxt[i])if (vis[e[i].v]&&e[i].v!=f){findrt(e[i].v,u,sum);siz[u]+=siz[e[i].v],maxp[u]=max(maxp[u],siz[e[i].v]);}if (sum-siz[u]>maxp[u]) maxp[u]=sum-siz[u];if (maxp[u]<maxp[root]) root=u;
}
int getsiz(int u,int f)
{int ans=1;for (int i=head[u];i;i=nxt[i])if (vis[e[i].v]&&e[i].v!=f)ans+=getsiz(e[i].v,u);return ans;
}
void build()
{vis[root]=0;int u=root;sub[u].push_back(u);for (int i=head[u];i;i=nxt[i])if (vis[e[i].v]){root=0;findrt(e[i].v,0,getsiz(e[i].v,0));int v=root;addnode(v,u);build();for (vector<int>::iterator it=sub[v].begin();it!=sub[v].end();++it) sub[u].push_back(*it);}vector<int> tmp1,tmp2;for (int i=0;i<(int)sub[u].size();i++){int v=sub[u][i];tmp1.push_back(dist(v,u)-r[v]);tmp2.push_back(dist(v,fa[u])-r[v]);}sort(tmp1.begin(),tmp1.end());getbuf(tmp1),SGT::build(rt[u]);if (fa[u]){sort(tmp2.begin(),tmp2.end());getbuf(tmp2),SGT::build(prt[u]);}
}
int main()
{read();int n=read();ll lans=0;for (int u=1;u<=n;u++){int a,c;a=read()^(lans%MOD),c=read(),r[u]=read();up[u][0]=a;for (int i=1;i<20;i++) up[u][i]=up[up[u][i-1]][i-1];dis[u]=dis[up[u][0]]+c,dep[u]=dep[up[u][0]]+1;if (u>1) addnode(u,a);addnode(u,a,c),addnode(a,u,c);printf("%lld\n",lans+=insert(u));if (pos==NULL) continue;
// cerr<<"rebuild "<<*pos<<'\n';int f=fa[*pos],id=idx[*pos];dfs(*pos),root=0,findrt(*pos,0,SGT::siz[rt[*pos]]);int RT=root;
// cerr<<*pos<<' '<<RT<<'\n';for (int i=0;i<(int)block.size();i++) {int v=block[i];SGT::dfs(rt[v]),SGT::dfs(prt[v]);fa[v]=idx[v]=rt[v]=prt[v]=0;son[v].clear(),sub[v].clear();}fa[RT]=f,idx[RT]=id,*pos=RT;build();block.clear();pos=NULL;} return 0;
}