[NOIP2024] 树上查询

[NOIP2024] 树上查询

先来考虑一个子问题

我们给定一个树,每次询问下标在一个区间的所有点的最近公共祖先的深度
定义v数组,\(v_i\)就表示第i个点和第i+1个点之间的最近公共祖先的深度,对于一个lr的询问,我们将lr-1的v值求一个最小值即为答案
这个做法为什么是对的呢,对于一个节点能够成为一个区间所有点的最近公共祖先,一定存在两个区间内的点分别存在于他的两个不同的子树内,并且区间内的所有点都存在于当前这颗子树下,所以至少有一对相邻点分别位于两个不同的子树,通过v数组一定能找到当前点

我们仔细观察一下v数组的性质

因为我们的询问是查询的最小值,所以对于一个v值,当我们查询的左右端点在一定区间内时,答案都是同一个v值,我们简称v区间,我们可以将这个区间预处理出来,显然,这个区间就是找到每个v值左右第一个大于它的v值,就是区间的左右端点
这个过程可以通过单调栈来优化到O(n)

重新考虑原题的查询

给出三个参数l,r,k,表示他想知道[l,r]中任意长度大于等于k的连续子区间的最近公共祖先深度的最大值
在先前v数组和区间的前提下,我们可以将问题转化为找到一个v区间,这个v区间满足

  1. 与查询区间的交区间长度>=k
  2. 在所有满足条件的v区间中找到v值最大的区间

暴力的解法就是遍历所有的区间,求一下交区间长度,然后在满足条件的区间内求一个v值最大
时间复杂度已经达到比较优秀的\(O(nq)\)了,可以拿到20pts的高分

优化部分

考虑我们的时间复杂度浪费在什么地方了,对于第二个条件很好求解,但是第一个条件即与查询区间的交区间长度>=k很难处理
我么可以对合法区间进行分讨,又tm出现两种情况,我们将查询区间左右端点表示为l,r,v区间左右端点表示为x,y

  1. 当前v区间的右端点在查询区间的右边,而v区间的左端点到查询区间的距离大于等于k,表示为

\[y>=r&&x<=r-k+1 \]

  1. 当前v区间的右端点都在查询区间右端点以左,查询区间左端点以右至少k的长度,同时自身长度大于等于k,表示为

\[l+k<=y<r&&y-x>=k \]

我们将v区间都挂到右端点上,第一次对r进行扫描线,第二次对k进行扫描线,统计答案
时间复杂度\(O(qlogn)\)
ACcode

using namespace std;
#define ll long long
#define debug cout<<"flyfree\n";
#define MAXN 500010
inline ll read(){
	ll x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
struct node_Sagtr{
	ll l[MAXN*4],r[MAXN*4],maxz[MAXN*4];
	void build(ll now,ll lz,ll rz){
		l[now]=lz,r[now]=rz,maxz[now]=0;
		if(lz==rz)return;
		ll mid=(lz+rz)/2;
		build(now*2,lz,mid);
		build(now*2+1,mid+1,rz);
	}
	void push_up(ll now){
		maxz[now]=max(maxz[now*2],maxz[now*2+1]);
	}
	void insert(ll now,ll p,ll val){
		if(l[now]==r[now]){
			maxz[now]=max(maxz[now],val);
			return;
		}
		ll mid=(l[now]+r[now])/2;
		if(p<=mid)insert(now*2,p,val);
		else insert(now*2+1,p,val);
		push_up(now);
	}
	ll find(ll now,ll lz,ll rz){
		if(l[now]>=lz&&r[now]<=rz)return maxz[now];
		ll mid=(l[now]+r[now])/2,ans=0;
		if(lz<=mid)ans=max(ans,find(now*2,lz,rz));
		if(rz>mid)ans=max(ans,find(now*2+1,lz,rz));
		return ans;
	}
};
ll hd[MAXN],ed[MAXN*2],nxt[MAXN*2];
ll n,m,idx;
ll dep[MAXN],fa[MAXN][50],v[MAXN];
ll l[MAXN],r[MAXN];
struct node{
	ll l,r,v,id,ans;
};
node qs[MAXN],line[MAXN];
bool cmp_r(node a,node b){
	return a.r>b.r;
}
bool cmp_l(node a,node b){
	return a.l<b.l;
}
bool cmp_len(node a,node b){
	return a.r-a.l>b.r-b.l;
}
bool cmp_id(node a,node b){
	return a.id<b.id;
}
bool cmp_k(node a,node b){
	return a.v>b.v;
}
void build(ll x,ll y){
	nxt[++idx]=hd[x];
	ed[idx]=y;
	hd[x]=idx;
}
void dfs(ll now,ll p){
	fa[now][0]=p;
	dep[now]=dep[p]+1;
	for(int i=1;i<=20;i++)fa[now][i]=fa[fa[now][i-1]][i-1];
	for(int i=hd[now];i;i=nxt[i]){
		ll y=ed[i];
		if(y==p)continue;
		dfs(y,now);
	}
}
ll lca(ll x,ll y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=20;i>=0;i--)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
	if(x==y)return x;
	for(int i=20;i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
deque <pair<ll,ll> > q;
node_Sagtr tr;
//void work_l(){
//	sort(qs+1,qs+1+m,cmp_l);
//	sort(line+1,line+n,cmp_l);
//	ll now=1;
//	tr.build(1,1,n);
//	for(int i=1;i<=m;i++){
//		while(now<n){
//			if(line[now].l<=qs[i].l){
//				tr.insert(1,line[now].r,line[now].v);
//				now++;
//			}else break;
//		}
//		qs[i].ans=max(qs[i].ans,tr.find(1,qs[i].l+qs[i].v-1,qs[i].r));
//	}
//}
void work_r(){
	sort(qs+1,qs+1+m,cmp_r);
	sort(line+1,line+n,cmp_r);
	ll now=1;
	tr.build(1,1,n);
	for(int i=1;i<=m;i++){
//		cout<<i<<endl;
		while(now<n){
			if(line[now].r>=qs[i].r){
				tr.insert(1,line[now].l,line[now].v);
				now++;
			}else break;
		}
		qs[i].ans=max(qs[i].ans,tr.find(1,1,qs[i].r-qs[i].v+1));
	}
}
void work_len(){
	sort(qs+1,qs+1+m,cmp_k);
	sort(line+1,line+n,cmp_len);
	ll now=1;
	tr.build(1,1,n);
	for(int i=1;i<=m;i++){
		if(qs[i].v==1&&qs[i-1].v>1){
			for(int j=1;j<=n;j++)tr.insert(1,j,dep[j]);
		}
		while(now<n){
			if(line[now].r-line[now].l+1>=qs[i].v){
				tr.insert(1,line[now].r,line[now].v);
				now++;
			}else break;
		}
		qs[i].ans=max(qs[i].ans,tr.find(1,qs[i].l+qs[i].v-1,qs[i].r));
	}
}
int main(){
	freopen("query4.in","r",stdin);
	freopen("w.out","w",stdout);
	n=read();
	for(int i=1;i<n;i++){
		ll x=read(),y=read();
		build(x,y);
		build(y,x);
	}
//	debug;
	dfs(1,0);
//	debug;
	for(int i=1;i<n;i++)v[i]=dep[lca(i,i+1)];
	q.push_back(make_pair(0,0));
	for(int i=1;i<n;i++){
//		cout<<i<<" "<<v[i]<<endl;
		while(!q.empty()&&q.back().second>=v[i])q.pop_back();
		l[i]=q.back().first+1;
		q.push_back(make_pair(i,v[i]));
	}
//	debug;
	while(!q.empty())q.pop_back();
	q.push_back(make_pair(n,0));
	for(int i=n-1;i;i--){
		while(!q.empty()&&q.back().second>=v[i])q.pop_back();
		r[i]=q.back().first;
		q.push_back(make_pair(i,v[i]));
	}
//	debug;
	for(int i=1;i<=n-1;i++){
		line[i].l=l[i],line[i].r=r[i],line[i].v=v[i];
//		cout<<"i:"<<i<<" dep:"<<v[i]<<endl;
	}
	m=read();
	for(int i=1;i<=m;i++){
		qs[i].l=read(),qs[i].r=read(),qs[i].v=read(),qs[i].id=i;
	}
//	work_l();
	work_r();
	work_len();
	sort(qs+1,qs+1+m,cmp_id);
//	for(int i=1;i<=m;i++)cout<<qs[i].ans<<endl;
	for(int i=1;i<=m;i++)printf("%lld\n",qs[i].ans);
	return 0;
}
posted @ 2024-12-06 22:13  flyfreemrn  阅读(111)  评论(0)    收藏  举报