P3920 [WC2014]紫荆花之恋 题解
这题真的非常 nb!
如果我们不管这个加叶子的操作,只考虑统计答案:每一次统计和点 \(i\) 能够成对的个数。这个可以用点分树做。注意到那个条件 \(dist(i,j)\leq r_i+r_j\) 相当于 \(dist(i,l)-r_i\leq r_j-dist(j,l)\),每个点统计到这个点的 \(dis-r\),拿 \(r_i-dis(i,j)\) 到 \(j\) 点去找排名即可,还有点分树的那个容斥。这个可以使用平衡树,也可以根号重构。根号重构的做法是,考虑每个点维护两个 vector
:\(big,small\),一开始把数都放到 \(small\) 里边,如果大小 \(>300\) 就全部倒进 \(big\) 里边并且排好序,这个过程可以使用归并排序。然后查一个数排名就在 \(big\) 里 upper_bound
,在 \(small\) 里暴力统计即可。复杂度是一个根号。
现在考虑如何处理这个动态的树。一个很自然的想法是类似替罪羊重构,如果 \(siz_u>\alpha\times siz_{fa_u}\),每次找到最浅的一个 \(u\),重构 \(fa_u\) 整个子树。这个过程的复杂度也不是很高,大概 \(2log\) 到 \(3log\)。实测取 \(\alpha=0.8\) 可以通过。
这道题在写的过程中出现了较多问题:
-
注意判断重构之前,要按照不重构的方式,把新加的点的所有数组都搞一遍。一开始的第一个点不要忘了特殊处理。
-
点分树的结构特殊,很容易想当然。点分树的一个子树在原树上是且仅是一个联通块,它四面八方都可能和其他连通块(在点分树上不相干的点)相邻,所以需要重构的话,就把每个子树都有哪些点记录下来就行了。
点击查看代码
inline void clearvector(std::vector<ll> &b){std::vector<ll> c;std::swap(b,c);}
inline void clearvector2(std::vector<int> &b){std::vector<int> c;std::swap(b,c);}
const int N=1e5+13,Mod=1e9;
const double alpha=0.8021;
struct Edge{int v,w,nxt;}e[N<<1];
int n,h[N],etot;
ll a[N];
inline void add_edge(int u,int v,int w){e[++etot]=(Edge){v,w,h[u]};h[u]=etot;}
namespace Tree{
const int logN=21;
int fa[N][logN],dep[N];ll dis[N];
inline int lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
for(int i=20;i>=0;--i)
if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
if(u==v) return u;
for(int i=20;i>=0;--i)
if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
inline ll dist(int u,int v){return dis[u]+dis[v]-2*dis[lca(u,v)];}
}
struct Node{
std::vector<ll> small,big;
inline void clear(){clearvector(small),clearvector(big);}
inline void insert(ll x){small.pb(x);}
inline int calc(ll x){
if(small.size()>300){
int lims=small.size(),limb=big.size();
std::vector<ll> tmp;tmp.resize(lims+limb);
std::sort(small.begin(),small.end());
int i=0,j=0,k=0;
while(i<lims||j<limb){
if(i>=lims) tmp[k]=big[j++];
else if(j>=limb) tmp[k]=small[i++];
else if(small[i]<=big[j]) tmp[k]=small[i++];
else tmp[k]=big[j++];
++k;
}
std::swap(big,tmp),clearvector(small);
}
int res=std::upper_bound(big.begin(),big.end(),x)-big.begin();
for(auto y:small) res+=(y<=x);
return res;
}
}ch[N],d[N];
int fa[N],siz[N],rt,maxx[N],psum;
bool vis[N];
std::vector<int> son[N];
void findrt(int u,int f){
siz[u]=1,maxx[u]=0;
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].v;if(v==f||vis[v]) continue;
findrt(v,u);
siz[u]+=siz[v];
maxx[u]=max(maxx[u],siz[v]);
}
maxx[u]=max(maxx[u],psum-siz[u]);
if(maxx[u]<maxx[rt]) rt=u;
}
void dfs(int u,int f,ll dis,Node &g1,Node &g2,std::vector<int> &g3){
g1.insert(dis-a[u]),g2.insert(dis-a[u]),g3.pb(u);
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].v;
if(v!=f&&!vis[v]) dfs(v,u,dis+e[i].w,g1,g2,g3);
}
}
void build(int u){
vis[u]=1;
son[u].clear();son[u].pb(u);
ch[u].clear();ch[u].insert(-a[u]);
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].v;if(vis[v]) continue;
rt=0,psum=siz[v];
findrt(v,0),findrt(rt,0);
fa[rt]=u;d[rt].clear();
dfs(v,0,e[i].w,ch[u],d[rt],son[u]);
build(rt);
}
}
int main(){
#ifdef LOCAL
freopen("P3920_3.in","r",stdin);
freopen("data.out","w",stdout);
#endif
int testcase;read(testcase);
read(n),read(a[1]),read(a[1]),read(a[1]);ch[1].insert(-a[1]);
ll lastans=0;println(lastans);Tree::dep[1]=1;siz[1]=vis[1]=1;son[1].pb(1);
for(int i=2;i<=n;++i){
int x,w;read(x),read(w),read(a[i]);x^=(lastans%Mod);
add_edge(x,i,w),add_edge(i,x,w);
Tree::fa[i][0]=fa[i]=x;Tree::dep[i]=Tree::dep[x]+1;Tree::dis[i]=Tree::dis[x]+w;
for(int p=i;p;p=fa[p]) siz[p]++,son[p].pb(i);
for(int j=1;j<=20;++j) Tree::fa[i][j]=Tree::fa[Tree::fa[i][j-1]][j-1];
ll ans=lastans;
for(int p=i;fa[p];p=fa[p]){
ll dd=Tree::dist(i,fa[p]);
ans+=ch[fa[p]].calc(a[i]-dd)-d[p].calc(a[i]-dd);
ch[fa[p]].insert(dd-a[i]),d[p].insert(dd-a[i]);
}
ch[i].insert(-a[i]),vis[i]=1;
int p=0;
for(int now=i;fa[now];now=fa[now])
if(siz[now]>alpha*siz[fa[now]]) p=fa[now];
if(p){
for(auto x:son[p]) vis[x]=0;
Node ttmp=d[p];int tttmp=fa[p];
maxx[rt=0]=INF,psum=siz[p];
findrt(p,0),findrt(rt,0);
d[rt]=ttmp,fa[rt]=tttmp;
build(rt);
}
// lastans=ans;
println(lastans=ans);
// printf("%d\n",i);
}
return 0;
}