HNOI2015 接水果 题解

思路

这道题是一道整体二分 + 扫描线 + 树链剖分(其实是 DFS 序)的综合题

首先,遇到这种“一条路径是另一条路径的子路径”问题,我们第一反应肯定是懵的。树上路径怎么比大小?怎么求第 k 小?这玩意看着就像个二维偏序,但路径它不是直的。

正男则反(强行套用),我们不去想水果怎么匹配盘子,而是想:一个盘子路径能接住哪些水果路径?

这里有一个极其关键的转化:利用 DFS 序把子树转化成区间。

对于一条路径 \((a,b)\),设 \(l = \text{lca}(a,b)\)。

我们先假设 \(a\) 和 \(b\) 不是祖先关系(也就是 \(l \neq a\) 且 \(l \neq b\))。

那么,如果一个水果路径 \((u,v)\) 要包含这个盘子 \((a,b)\),\(u\) 必须落在 \(a\) 的子树里,\(v\) 必须落在 \(b\) 的子树里(或者反过来,但因为我们只关心无序路径,所以把两边端点匹配好)。

转化成 DFS 序就是:

  • \(dfn[u] \in [dfn[a], dfn[a]+siz[a]-1]\)
  • \(dfn[v] \in [dfn[b], dfn[b]+siz[b]-1]\)

你看,一个盘子就变成了一个矩形!横坐标是 \(a\) 的子树区间,纵坐标是 \(b\) 的子树区间。

主播主播,那如果 \(a\) 是 \(b\) 的祖先呢?

这十分地白痴,但我实际中确实被这个卡了。

如果 \(a\) 是 \(b\) 的祖先(假设 \(a\) 在上),那么盘子路径是 \(a \to b\)。水果要包含它,一端(假设是 \(u\))必须在 \(b\) 的子树里,但另一端 \(v\) 不能在 \(a\) 的子树里随变选——因为如果 \(v\) 也选在 \(a\) 的子树里的某个分支,路径会变短,包含不了整条 \(a \to b\)。

实际上,\(v\) 必须选在 \(a\) 子树挖掉 \(a\) 到 \(b\) 路径上第一个儿子 \(c\) 的子树 之外的部分。

所以这就会拆成两个矩形:

  1. \([dfn[b], dfn[b]+siz[b]-1] \times [1, dfn[c]-1]\)
  2. \([dfn[b], dfn[b]+siz[b]-1] \times [dfn[c]+siz[c], n]\)

(记得把两个维度都填满,因为是无序的,所以横纵坐标要对称,或者统一把 \(dfn\) 小的放前面,防止重复计算。)

那现在问题就简单了:

  • 盘子 \(\longrightarrow\) 带权值的矩形(权值就是 \(c_i\))
  • 水果 \(\longrightarrow\) 一个点 \((dfn[u], dfn[v])\)

我们要回答的是:覆盖这个点的所有矩形中,权值第 \(k\) 小的那个是多少?

这就是一个经典的整体二分 + 二维数点问题。

我们二分答案权值 \(mid\),把权值 \(\le mid\) 的盘子(矩形)加入扫描线(树状数组维护差分),然后对于每个水果(点),查询这个点被多少个矩形覆盖。如果覆盖数 \(\ge k\),说明答案在左区间;否则 \(k\) 减去覆盖数,去右区间。

整体二分的时间复杂度是 \(O((p+q) \log^2 n)\),因为每次扫描线要 \(\log n\),整体二分要 \(\log V\)。
接水果这道题最难的地方是把路径转化成矩形的分类讨论

Code

非常的丑陋

#include<bits/stdc++.h>

using namespace std;
int n,p,q,dfn[40010],dep[40010],siz[40010],ba[40010][17],dfntot;
vector<int>e[40010];
int li[200010],lcnt,rt[200010];
inline void dfs(int u){
	for(int i=1;i<17;i++)ba[u][i]=ba[ba[u][i-1]][i-1];
	dfn[u]=++dfntot;
	siz[u]=1;
	for(auto v:e[u]){
		if(v==ba[u][0])continue;
		ba[v][0]=u;
		dep[v]=dep[u]+1;
		dfs(v);
		siz[u]+=siz[v];
	} 
}
inline int lca(int u,int v){
	if(dep[u]<dep[v])swap(u,v);
	for(int i=16;i>=0;i--)if(dep[u]-dep[v]-1>=(1<<i))u=ba[u][i];
	if(ba[u][0]==v)return u;
	u=ba[u][0];
	for(int i=16;i>=0;i--)if(ba[u][i]!=ba[v][i])u=ba[u][i],v=ba[v][i];
	return u;
}
inline int lowbit(int x){
	return x&-x;
}
int sum[10000010],tot,ls[10000010],rs[10000010];
inline void change(int &k,int l,int r,int pos,int val){
	if(!k)k=++tot;
	sum[k]+=val;
	if(l==r)return;
	int mid(l+r>>1);
	if(pos<=mid)change(ls[k],l,mid,pos,val);
	else change(rs[k],mid+1,r,pos,val);
}
int bb[40010],bcnt;
inline int query(int l,int r,int val){
	if(l==r)return l;
	int t=0;
	for(int i=1;i<=bcnt;i++)t+=sum[ls[bb[i]]];
	int mid(l+r>>1);
	if(val<=t){
		for(int i=1;i<=bcnt;i++)bb[i]=ls[bb[i]];
		return query(l,mid,val);
	}
	for(int i=1;i<=bcnt;i++)bb[i]=rs[bb[i]];
	return query(mid+1,r,val-t);
}
inline void modify(int l,int r,int val,int z){
	for(int i=l;i<=n;i+=lowbit(i))change(rt[i],1,lcnt,val,z);
	for(int i=r+1;i<=n;i+=lowbit(i))change(rt[i],1,lcnt,val,-z);
}
inline int ask(int l,int r){
	bcnt=0;
	for(int i=l;i>=1;i-=lowbit(i))bb[++bcnt]=rt[i];
	return query(1,lcnt,r);
}
struct no{
	int typ,x,l,r,a,id;
}que[300010];
int qcnt,ans[200010];
inline void cutup(int x1,int y1,int x2,int y2,int w){
	if(x1>x2 or y1>y2)return;
	que[++qcnt]={2,x2,y1,y2,w};
	que[++qcnt]={0,x1,y1,y2,w};
	que[++qcnt]={0,y1,x1,x2,w};
	que[++qcnt]={2,y2,x1,x2,w};
}
int t1[40010],t2[40010],t3[40010];
main(){
	cin.tie(0)->ios::sync_with_stdio(0);
	cin>>n>>p>>q;
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dfs(1);
	for(int i=1;i<=p;i++){
		int a,b,c;
		cin>>a>>b>>c;
		t1[i]=a,t2[i]=b,t3[i]=c;
		li[++lcnt]=c;
	} 
	sort(1+li,1+li+lcnt);
	lcnt=unique(1+li,1+li+lcnt)-li-1;
	for(int i=1;i<=p;i++){
		int a=t1[i],b=t2[i],c=lower_bound(1+li,1+li+lcnt,t3[i])-li;
		if(dep[a]>dep[b])swap(a,b);
		if(dfn[a]+siz[a]-1>=dfn[b] and dfn[a]<=dfn[b]){
			int x=lca(a,b);
			//cout<<1<<' '<<dfn[x]-1<<' '<<dfn[b]<<' '<<dfn[b]+siz[b]-1<<'\n';
			cutup(1,dfn[b],dfn[x]-1,dfn[b]+siz[b]-1,c);
			cutup(dfn[x]+siz[x],dfn[b],n,dfn[b]+siz[b]-1,c);
		}else cutup(dfn[a],dfn[b],dfn[a]+siz[a]-1,dfn[b]+siz[b]-1,c);
	}
	for(int i=1;i<=q;i++){
		int u,v,k;
		cin>>u>>v>>k;
		que[++qcnt]={1,dfn[u],dfn[v],dfn[v],k,i};
	}
	sort(1+que,1+que+qcnt,[&](no x,no y){
		if(x.x==y.x)return x.typ<y.typ;
		return x.x<y.x;
	});
	for(int i=1;i<=qcnt;i++){
		if(que[i].typ==0)modify(que[i].l,que[i].r,que[i].a,1);
		if(que[i].typ==1)ans[que[i].id]=ask(que[i].l,que[i].a);
		if(que[i].typ==2)modify(que[i].l,que[i].r,que[i].a,-1);
	}
	for(int i=1;i<=q;i++)cout<<li[ans[i]]<<'\n';
	return 0;
}
posted @ 2026-07-07 20:00  zoupiwa666  阅读(12)  评论(1)    收藏  举报