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  阅读(8)  评论(1)    收藏  举报