链接_
终于过了。。。
首先这题显然只能一个个处理,即每加入一个点计算贡献该点与其他点的贡献。
考虑转换题意:假设我们找到一个 (p) 在 (u ightarrow v) 路径上,那么 (dis(u,v)=dis(u,p)+dis(p,v))。
那么就有 (dis(u,p)+dis(v,p)leq d_u+d_v),移项得 (d_u-dis(u,p)geq dis(v,p)-d_v)。
我们假设 (u) 是我们当前的加入点,那么我们令 (w_{p,v}=dis(v,p)-d_v),最后我们只需要对于所有 (p) 求其中 (leq d_u-dis(u,p)) 的个数即可。这个就是一个平衡树维护的东西。
直接计算显然是 (O(n^2log n))。但按照套路,考虑点分树,可以把需要计算的 (p) 缩小到 (O(log n)) 个。而且根据点分治的性质,(u) 到 (v) 的路径上有且仅有一个点在点分树上同时为 (u,v) 的祖先,所以可以计算。
但是我们还有一个前提是 (p) 在 (u ightarrow v) 路径上。如果直接按上述方式计算,上层分治中心也会计算这个值。按照点分治的套路,考虑容斥,即对于当前待插入的点 (u),我们在它的每个点分祖先 (p) 上都再维护一个平衡树表示容斥的标记,然后当处理到 (p) 时,我们减去这个标记,表示 (u) 已经处理过了,抵消上层分治中心的处理 (u) 的贡献。
这样就处理完了点分树的部分,时间复杂度 (O(nlog^2n))。但是当你兴奋地打完代码后,才发现这题最毒瘤的地方:强制在线。(毒瘤题居然不给离线一分)。
对于这类强制在线题目有一个比较暴力的通解,就是根号重构。但是这里会退化成 (O(nsqrt nlog^2 n)) 显然爆炸。
但是我们考虑,虽然动态加点可能会导致整颗点分树全部发生变化,但是我们并不需要它一定是点分树,我们只需要它的树深是 (O(log n)) 的就好了。这个在平衡树中有一个很经典的数据结构:替罪羊树。
具体来说,我们定义一个常数 (alpha),只有其中一颗子树的大小超过整棵树的 (n imesalpha),我们才对这棵树进行重建。
所以插入一个点时,我们对它在点分树上的祖先找到第一个不平衡的树,然后对这棵子树重新构造点分树。
在替罪羊树中 (alpha=0.7) 左右比较优秀,但是这里要稍微大一些,因为一次重构的代价是 (O(s_ulog^2 s_u)) 的。
具体复杂度我也不会证,但好像均摊下来不超过 (O(nlog^3 n)) ,稍微卡卡常就能过。
还有这里如果用splay/非旋treap会被卡常。。。
当然写有旋treap就没这么多事了。
然后由于这题有很多次重构,总重构复杂度会到 (O(nlog^3 n)),但是实际用的点只有 (O(nlog^2 n)),所以要垃圾回收。
最后由于要重构很多次,rand()好像有点慢。。。不如直接模仿替罪羊树的写法,不平衡就旋(怎么感觉在哪里看到过这种操作),也不知道为什么这样就会变快?
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define N 100010
#define M N*40
using namespace std;
const double alp=0.85;
int nxt[N<<1],to[N<<1],w[N<<1],head[N],cnt;
void add(int u,int v,int w1)
{
nxt[++cnt]=head[u];
to[cnt]=v;
w[cnt]=w1;
head[u]=cnt;
}
int val[M],rnd[M],ch[M][2],siz[M],tot;
int sta[M],totp;
void update(int u){siz[u]=siz[ch[u][0]]+siz[ch[u][1]]+1;}
int new_node(int v)
{
int u=totp?sta[totp--]:++tot;
// if(tot>M-10) throw;
// rnd[u]=rand();
val[u]=v;siz[u]=1;ch[u][0]=ch[u][1]=0;
return u;
}
void rotate(int &u,int d)
{
int v=ch[u][d];
// if(rnd[u]<rnd[v]) return;
if(siz[u]*0.75>siz[v]) return;
ch[u][d]=ch[v][!d],ch[v][!d]=u;
update(u);update(v);u=v;
}
void insert(int &u,int v)
{
if(!u){u=new_node(v);return;}
siz[u]++;
if(v<=val[u]) insert(ch[u][0],v),rotate(u,0);
else insert(ch[u][1],v),rotate(u,1);
}
void clear(int &u){if(!u) return;clear(ch[u][0]),clear(ch[u][1]);sta[++totp]=u;u=siz[u]=rnd[u]=0;}
int find(int u,int v)
{
if(!u) return 0;
if(val[u]<=v) return find(ch[u][1],v)+siz[ch[u][0]]+1;
return find(ch[u][0],v);
}
int r1[N],r2[N],asiz[N];
int vis[N],ut;
int usiz,root,msiz;
void dfs1(int u,int f)
{
clear(r1[u]),clear(r2[u]);asiz[u]=1;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(vis[v]==ut || v==f) continue;
dfs1(v,u);
asiz[u]+=asiz[v];
}
}
void dfs2(int u,int f)
{
int res=usiz-asiz[u];
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(vis[v]==ut || v==f) continue;
dfs2(v,u);
res=max(res,asiz[v]);
}
if(res<msiz) msiz=res,root=u;
}
int qu[N],qd[N],qtot;
void dfs3(int u,int f,int d)
{
qu[++qtot]=u,qd[qtot]=d;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(vis[v]==ut || v==f) continue;
dfs3(v,u,d+w[i]);
}
}
int dep[N],c[N];
#define D 110
int f[N][D],fd[N][D];
void rebuild(int u,int d)
{
dfs1(u,0);
usiz=msiz=asiz[u];root=u;
dfs2(u,0);
qtot=0;
dep[u=root]=d;
vis[u]=ut;
dfs3(u,0,0);
for(int i=1;i<=qtot;i++)
{
int v=qu[i];
f[v][d]=u,fd[v][d]=qd[i];
insert(r1[u],fd[v][d]-c[v]);
if(d) insert(r2[u],fd[v][d-1]-c[v]);
}
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(vis[v]!=ut) rebuild(v,d+1);
}
}
int main()
{
int n;
scanf("%*d%d%*d%*d%d",&n,&c[1]);
f[1][0]=1,insert(r1[1],-c[1]);
puts("0");
long long ans=0;
for(int i=2;i<=n;i++)
{
int fa,w;
scanf("%d%d%d",&fa,&w,&c[i]);fa^=ans%1000000000;
add(fa,i,w),add(i,fa,w);
f[i][dep[i]=dep[fa]+1]=i,fd[i][dep[i]]=0;
if(dep[i]>D-10) throw;
for(int j=0;j<dep[i];j++)
{
f[i][j]=f[fa][j];
fd[i][j]=fd[fa][j]+w;
ans+=find(r1[f[i][j]],c[i]-fd[i][j]);
if(j) ans-=find(r2[f[i][j]],c[i]-fd[i][j-1]);
}
for(int j=0;j<=dep[i];j++)
{
insert(r1[f[i][j]],fd[i][j]-c[i]);
if(j) insert(r2[f[i][j]],fd[i][j-1]-c[i]);
}
++ut;
for(int j=0;j<dep[i];vis[f[i][j]]=ut,j++)
if(siz[r1[f[i][j]]]*alp<siz[r1[f[i][j+1]]]){rebuild(f[i][j],j);break;}
printf("%lld
",ans);
}
return 0;
}