题目链接:http://acm.zzuli.edu.cn/problem.php?id=2520
题目大意:中文题,比较容易读懂。
思路:由于这个是离线的查找,经观察发现,Ans[u]=Sum(Ans[u的第一子节点])+Query(A[u]-k,A[u]+k)的数量。
那么我们就很容易的想到用权值线段树维护每个元素的数量,然后直接查询元素值在[A[u]-k,A[u]+k]的数量就可以了。
由于给出的元素值较大,因此我们要将这些元素离散一下。
我们访问到一个新的节点的时候,注意要先减去权值线段树中满足区间的点,这样保证修改之后,加上查询的点就是该子树的点对了。
如何获得区间,我们可以二分出符合要求的区间,然后直接修改即可。
ACCode:
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
#define Pair pair<ll,ll>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// ??
//std::ios::sync_with_stdio(false);
// register
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
class WeiSegment{
ll Tree[MAXN<<2];
public:
void Build(){
clean(Tree,0);
}
void Update(int p,int l,int r,int rt){
if(l==r){
Tree[rt]++;
return ;
}
int mid=(l+r)>>1;
if(p<=mid) Update(p,l,mid,rt<<1);
if(p>mid) Update(p,mid+1,r,rt<<1|1);
Tree[rt]=Tree[rt<<1]+Tree[rt<<1|1];
}
ll Query(int ql,int qr,int l,int r,int rt){
if(ql<=l&&r<=qr){
return Tree[rt];
}
int mid=(l+r)>>1;
ll ans=0;
if(ql<=mid) ans+=Query(ql,qr,l,mid,rt<<1);
if(qr>mid) ans+=Query(ql,qr,mid+1,r,rt<<1|1);
return ans;
}
void Show(int l,int r,int rt){
printf("l=%d r=%d rt=%d: Tree[rt]=%d\n",l,r,rt,Tree[rt]);
if(l==r) return ;
int mid=(l+r)>>1;
Show(l,mid,rt<<1);Show(mid+1,r,rt<<1|1);
}
};
struct Node1{
ll v,val,nxt;
Node1(ll _v=0,ll _val=0,ll _nxt=0){
v=_v;val=_val;nxt=_nxt;
}
};
Node1 Edge[MAXN<<2];
int Head[MAXN],Ecnt;
ll A[MAXN],B[MAXN];
ll Ans[MAXN];
map<ll,ll> LtoI;ll Icnt;
WeiSegment WSeg;
ll n,k;
void Intt(){
clean(Head,-1);Ecnt=0;
LtoI.clear();Icnt=0;
WSeg.Build();
}
void AddEdge(ll u,ll v,ll val){
Edge[Ecnt]=Node1(v,val,Head[u]);
Head[u]=Ecnt++;
}
int GetL(ll Key){
int l=1,r=n,mid;
while(l<=r){
int mid=(l+r)>>1;
if(A[mid]>=Key) r=mid-1;
else l=mid+1;
}return l;
}
int GetR(ll Key){
int l=1,r=n,mid;
while(l<=r){
int mid=(l+r)>>1;
if(A[mid]>Key) r=mid-1;
else l=mid+1;
}return r;
}
void DFS(int u){//到该点了
// printf("u=%d :\n",u);
ll res=0;
for(int i=Head[u];i+1;i=Edge[i].nxt){//刷新该点的子树
int temp=Edge[i].v;
int ql=GetL(B[temp]-k),qr=GetR(B[temp]+k);//返回在A中的位置
Ans[temp]-=WSeg.Query(LtoI[A[ql]],LtoI[A[qr]],1,Icnt,1);//取消前置节点
// printf("temp=%d,B[temp]=%lld ql,qr: %d %d Ans[%d]=%lld\n",temp,B[temp],ql,qr,temp,Ans[temp]);
DFS(temp);
res+=Ans[temp];
}//子树都修改完了 ,Ans[u]=Sum(Ans[第一子节点])+Query(A[u]-k,A[u]+k);
WSeg.Update(LtoI[B[u]],1,Icnt,1);//刷新该点的值
// printf("Update:B[u]=%lld,LtoI[%lld]=%lld\n",B[u],B[u],LtoI[B[u]]);WSeg.Show(1,Icnt,1);printf("End Update\n");
int ql=GetL(B[u]-k),qr=GetR(B[u]+k);//可选择的节点在A[ql]和A[qr].second之间,查询数量即可
Ans[u]+=res+WSeg.Query(LtoI[A[ql]],LtoI[A[qr]],1,Icnt,1);
// printf("Ans[%d]=%d=%lld(res)+%lld(Query)\n",u,Ans[u],res,WSeg.Query(LtoI[A[ql]],LtoI[A[qr]],1,Icnt,1));
}
int main(){
scanf("%lld%lld",&n,&k);Intt();
for(int i=1;i<=n;++i){
scanf("%lld",&A[i]);B[i]=A[i];
}sort(A+1,A+1+n);
for(int i=1;i<=n;++i){
if(LtoI[A[i]]) continue;
LtoI[A[i]]=++Icnt;
}
// printf("Show:LtoI: ");for(int i=1;i<=n;++i) printf("%lld ",LtoI[B[i]]);printf("\n");
for(int i=1;i<n;++i){
int fa;scanf("%d",&fa);
AddEdge(fa,i+1,1);
}DFS(1);
for(int i=1;i<=n;++i){
printf("%lld\n",Ans[i]);
}
}