题解:
这是经典的KD树模型,所以直接KD树优化建边即可。
不过需要注意的是KD树上的点不能是原来的点,要新建一个映射点连向原来的点,因为原来的点如果增加了本来没有的连边会对答案有影响。。
(刚才搜了搜好像全是线段树?? 赶紧去学一学)
upt::
线段树做法,可持久化线段树合并,比kd树好打多了。。
Code : https://paste.ubuntu.com/26511273/
#include <bits/stdc++.h>
using namespace std;
inline int rd() {
char ch=getchar(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
return i*f;
}
const int N=2e5+50;
int n,tot,dfn[N],dep[N],sze[N],id[N],ind;
int scc,xc[N],d[N],low[N],st[N],ins[N],tp;
long long ans;
struct point {
int x,y;
point(){}
point(int x,int y):x(x),y(y){}
}que[N];
inline bool cmpx(const point &a,const point &b) {return a.x<b.x;}
inline bool cmpy(const point &a,const point &b) {return a.y<b.y;}
struct node {
node *lc,*rc;
point p;
int o;
int mx[2],mn[2];
inline void upt(node *x,node *y) {
y->mx[0]=max(y->mx[0],x->mx[0]);
y->mx[1]=max(y->mx[1],x->mx[1]);
y->mn[0]=min(y->mn[0],x->mn[0]);
y->mn[1]=min(y->mn[1],x->mn[1]);
}
inline void upt() {
mx[0]=(mn[0]=p.x);
mx[1]=(mn[1]=p.y);
if(lc)upt(lc,this);
if(rc)upt(rc,this);
}
}nd[N],*rt=nd+1,*pool=rt;
struct Graph*G;
struct Graph{
vector <int> edge[N];
inline void init(int sze) {
for(int i=1;i<=sze;i++) edge[i].clear();
}
inline void add(int x,int y) {
if(x==y) return;
edge[x].push_back(y);
}
inline void dfs(int x) {
dfn[x]=++ind; id[ind]=x; sze[x]=1;
for(int e=edge[x].size()-1;e>=0;e--) {
int v=edge[x][e];
dep[v]=dep[x]+1;
dfs(v); sze[x]+=sze[v];
}
que[x]=point(dfn[x],dep[x]);
}
inline void build(node *&now,int l,int r,int dim) {
now=++pool;
now->o=++tot;
if(l==r) {
now->p=que[l];
G->add(tot,id[now->p.x]);
now->upt();
return;
} int mid=(l+r)>>1;
if(dim) nth_element(que+l,que+mid,que+r+1,cmpy);
else nth_element(que+l,que+mid,que+r+1,cmpx);
now->p=que[mid];
G->add(tot,id[now->p.x]);
if(l<mid) build(now->lc,l,mid-1,dim^1),G->add(now->o,now->lc->o);
if(r>mid) build(now->rc,mid+1,r,dim^1),G->add(now->o,now->rc->o);
now->upt();
}
inline int cmp(const point &mn,const point &mx,node *now) {
if(now->mx[0]<mn.x||now->mx[1]<mn.y) return -1;
if(now->mn[0]>mx.x||now->mn[1]>mx.y) return -1;
if(now->mn[0]>=mn.x&&now->mn[1]>=mn.y&&now->mx[0]<=mx.x&&now->mx[1]<=mx.y) return 1;
return 0;
}
inline bool in(const point &mn,const point &mx,const point &p) {
return p.x>=mn.x&&p.x<=mx.x&&p.y>=mn.y&&p.y<=mx.y;
}
inline void findarea(node *now,int i,const point &mn,const point &mx) {
if(!now) return;
int t=cmp(mn,mx,now);
if(t==-1) return;
if(t==1) G->add(i,now->o);
else {
findarea(now->lc,i,mn,mx);
findarea(now->rc,i,mn,mx);
if(in(mn,mx,now->p)) G->add(i,id[now->p.x]);
}
}
inline void build() {
ind=0; dfs(1);
G->init(2*n);
pool=rt;
for(int i=1;i<=n;i++) nd[i].lc=NULL,nd[i].rc=NULL;
build(rt,1,n,0);
for(int i=1;i<=n;i++) {
findarea(rt,i,point(dfn[xc[i]],dep[xc[i]]),point(dfn[xc[i]]+sze[xc[i]]-1,dep[xc[i]]+d[i]));
}
}
inline void tarjan(int x) {
dfn[x]=(low[x]=++ind); ins[x]=1; st[++tp]=x;
for(int e=edge[x].size()-1;e>=0;e--) {
int v=edge[x][e];
if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
else if(ins[v]) low[x]=min(low[x],dfn[v]);
}
if(low[x]==dfn[x]) {
++scc; sze[scc]=0;
int u;
do{
u=st[tp--];
ins[u]=0;
if(u<=n) ++sze[scc];
}while(u!=x);
ans+=((long long)sze[scc]*(sze[scc]-1)/2);
}
}
inline void tarjan() {
ind=0; scc=0;
memset(dfn+1,0,sizeof(int)*tot);
for(int i=1;i<=tot;i++) if(!dfn[i])
tarjan(i);
}
}tr,gh;
inline void solve() {
n=rd(); tot=n;
tr.init(n);
for(int i=2;i<=n;i++) tr.add(rd(),i);
for(int i=1;i<=n;i++) xc[i]=rd(),d[i]=rd();
tr.build();
ans=0;
gh.tarjan();
printf("%lld\n",ans);
}
int main() {
G=&gh;
int T=rd();
while(T--) solve();
}