一棵树,每条边有边权,点有点权(r),(n)次加入一个点,给出它与父亲的连边长度和它的点权,问此时总共有多少对点满足(r_i+r_jge dist(i,j))。(nle 10^5)。
分析
树上点对统计可以使用点分治。每次只需要统计新增了多少对满足条件的点。设一对点((u,v))的lca为(k),那么条件可以转化为(r_u-dist(u,k)ge dist(v,k)-r_v),每次新加入点直接挂在父亲下面并直接往上跳,设当前跳到点(c),只要统计经过这个点有多少满足条件的即可。我们拿着(r_u-dist(u,k))去查询有多少个(dist(v,k)-r_v)小于等于它就好了。用平衡树来维护这个东西,查询完之后就把它插入平衡树里。
但这样明显会重复计算从同一个子树来的点,所以在每个点还要再开一个平衡树,记录一下子树中到这个点的父亲的值,减一下即可。
上面我们说直接把这个点挂在下面,这事实上是很不对的,一个链的情况就可以把这个卡掉。于是我们可以利用重构的思想,如果一个子树的大小大于它父亲子树大小的(alpha),那么就找到最上面一个这样的位置,重构整颗子树,把它变成一个点分治树的样子。替罪羊树的时间复杂度分析说明这样做的复杂度为(O(f(n)log _{frac{1}{alpha}}n)),其中(f(n))为重构大小为(n)的子树的复杂度。
这一题中,点分治树的深度是(O(logn))的,所以一次重构的可以直接每个点往上跳(O(logn))个父亲,每次直接向平衡树中插入,总复杂度为(O(nlog^3n))。
代码
弃坑啦!!!!写过splay,treap,奇怪重量平衡树,最后改成替罪羊树来作平衡树,现在uoj上80分。
不过学到了一种点分治找中心的神奇写法,超级好写,只要两行~
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long giant;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
void write(giant x) {
if (!x) puts("0"); else {
static char s[25];
int tot=0;
while (x) s[++tot]=x%10+'0',x/=10;
while (tot) putchar(s[tot--]);
puts("");
}
}
const int maxn=1e5+1;
const int maxm=5e6;
const int maxj=17;
const double alpha=0.77;
giant ans=0;
int fa[maxn],d[maxn],rec[maxn],size[maxn],my[maxn],onf[maxn],n;
bool able[maxn];
vector<int> g[maxn];
inline int conv(int x) {
return x^((int)(ans%(giant)1e9));
}
struct graph {
int h[maxn],tot,f[maxn][maxj],dep[maxn],dis[maxn];
graph ():tot(0) {
for (int i=0;i<maxj;++i) f[1][i]=1;
}
void add(int u,int v,int w) {
g[u].push_back(v);
g[v].push_back(u);
f[v][0]=u;
for (int j=1;j<maxj;++j) f[v][j]=f[f[v][j-1]][j-1];
dep[v]=dep[u]+1,dis[v]=dis[u]+w;
}
int lca(int x,int y) {
if (dep[x]<dep[y]) swap(x,y);
for (int j=maxj-1;j>=0;--j) if (dep[f[x][j]]>=dep[y]) x=f[x][j];
if (x==y) return x;
for (int j=maxj-1;j>=0;--j) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
return f[x][0];
}
int dist(int x,int y) {
return dis[x]+dis[y]-2*dis[lca(x,y)];
}
} G;
int aux[maxn];
struct Treap {
struct node {
int ch[2],fa,val,size;
} t[maxm];
int pool[maxm],per;
Treap ():per(0) {
for (int i=1;i<maxm;++i) pool[i]=i;
}
inline void update(int x) {
t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1;
}
inline void delnode(int &x) {
if (!x) return;
t[x].ch[0]=t[x].ch[1]=t[x].fa=t[x].val=t[x].size=0;
pool[per--]=x;
x=0;
}
inline int newnode(int x) {
int nw=pool[++per];
t[nw].size=1;
t[nw].val=x;
return nw;
}
bool rson(int x) {
return t[t[x].fa].ch[1]==x;
}
void travel(int x,int &len) {
if (!x) return;
travel(t[x].ch[0],len);
aux[++len]=t[x].val;
travel(t[x].ch[1],len);
delnode(x);
}
int reb(int l,int r) {
if (l>r) return 0;
int mid=l+r>>1;
int nw=newnode(aux[mid]);
t[nw].ch[0]=reb(l,mid-1);
t[nw].ch[1]=reb(mid+1,r);
update(nw);
return nw;
}
void rebid(int x) {
int len=0;
travel(x,len);
int fat=t[x].fa;
bool d=rson(x);
int l=1,r=len;
t[fat].ch[d]=reb(l,r);
update(fat);
}
inline void insert(int &rt,int x) {
int nw=newnode(x);
if (!rt) {
rt=nw;
return;
}
int now=rt;
while (true) {
++t[now].size;
int &tmp=t[now].ch[x>t[now].val];
if (tmp) now=tmp; else {
tmp=nw;
break;
}
}
int goat=0;
for (;t[now].fa;now=t[now].fa) if (t[now].size>0.6*t[t[now].fa].size) goat=now;
if (goat) rebid(goat);
}
inline int le(int now,int x) {
int ret=0;
while (now) {
if (x>t[now].val) ret+=t[t[now].ch[0]].size+1; else
if (x==t[now].val) ++ret;
now=t[now].ch[x>t[now].val];
}
return ret;
}
void clear(int &x) {
if (x) clear(t[x].ch[0]),clear(t[x].ch[1]);
delnode(x);
}
} tree;
inline void addit(int u,int v,int w) {
G.add(u,v,w);
fa[v]=u,d[v]=d[u]+1,size[v]=1;
}
inline bool bad(int x) {
return size[x]>alpha*size[fa[x]];
}
void label(int x,int f,int no) {
able[x]=true;
for (int v:g[x]) if (v!=f && d[v]>d[no]) label(v,x,no);
}
int Size(int x,int f) {
size[x]=1;
for (int v:g[x]) if (v!=f && able[v]) size[x]+=Size(v,x);
return size[x];
}
int Root(int x,int f,int hf) {
for (int v:g[x]) if (v!=f && able[v] && size[v]>hf) return Root(v,x,hf);
return x;
}
void rebuild(int x,int fat,int up) {
int sz=Size(x,0),rt=Root(x,0,sz>>1);
able[rt]=false,d[rt]=d[fa[rt]=fat]+1,size[rt]=size[x];
tree.clear(my[rt]),tree.clear(onf[rt]);
for (int v:g[rt]) if (able[v]) rebuild(v,rt,up);
for (int y=rt;y!=up;y=fa[y]) {
tree.insert(my[y],G.dist(y,rt)-rec[rt]);
if (!fa[y]) break;
tree.insert(onf[y],G.dist(fa[y],rt)-rec[rt]);
}
}
int cs=0;
inline void work(int x) {
int goat=0;
for (int y=x;y;y=fa[y]) {
int d=G.dist(x,y);
ans+=tree.le(my[y],rec[x]-d);
tree.insert(my[y],d-rec[x]);
if (!fa[y]) break;
d=G.dist(x,fa[y]);
ans-=tree.le(onf[y],rec[x]-d);
tree.insert(onf[y],d-rec[x]);
++size[fa[y]];
if (bad(y)) goat=y;
}
if (goat) ++cs,label(fa[goat],0,fa[fa[goat]]),rebuild(fa[goat],fa[fa[goat]],fa[fa[goat]]);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("my.out","w",stdout);
#endif
srand(200);
puts("0");
read(),n=read(),read(),read(),rec[1]=read();
tree.insert(my[1],-rec[1]),size[1]=1;
for (int i=2;i<=n;++i) {
// int x=conv(read());
int x=read();
int c=read();
rec[i]=read();
addit(x,i,c);
work(i);
write(ans);
}
return 0;
}