ZZULI-2520-大小接近的点对(数据离散+权值线段树+二分+DFS搜索序列)

题目链接: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]);
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值