哩哩哩啦哩啦(lirililarila)

哩****啦

题目描述

给定一棵有 \(n\) 个点的树,根为 \(1\),设 \(dep_i\) 表示点 \(i\) 的深度。定义 \(S_{i,k}=\{x|lca(x,i)=i\wedge dep_x-dep_i=k\}\)(即点 \(i\)\(k\) 级后代组成的点集)。再给出 \(m\) 个要求,第 \(i\) 个为 \((u_i,k_i)\)。你需要找到一对正整数 \(L,R\),使得它们满足以下条件:

  1. \(L\le R\)
  2. \(\forall i\in[1,m]\wedge i\in \mathbb N,S_{u_i,k_i}\cap[L,R]\neq\varnothing\)
  3. 在满足前两条的前提下,最小化 \(R-L\)
  4. 在满足前三条的前提下,最小化 \(L\)

数据范围

\(1\le n,m\le2\times10^5\)\(1\le u_i,k_i\le n\)

时空限制

\(2\text{s}\)\(512\text{Mb}\)

做法

提供一个 \(O(n\log n)\) 的做法。

容易发现,如果一对要求 \((i,j)\) 满足 \(u_i\)\(u_j\) 的祖先且 \(dep_{u_i}+k_i=dep_{u_j}+k_j\)(下文我们称之为“发生冲突”),那么要求 \(i\) 是没用的,因为此时一定有 \(S_{u_j,k_j}\subseteq S_{u_i,k_i}\),所以如果第 \(j\) 条要求被满足,那么第 \(i\) 条也一定会被满足,于是可以把要求 \(i\) 扔掉。这样一来,树上的每个点最多被一条留下来的要求覆盖。于是我们把问题转化成了两部分,第一部分是去掉无用的要求,第二部分是要在若干个大小总和不超过在 \(O(n)\) 级别的集合中从每个集合里选出一个数,使得选出的数极差最小且在极差最小的同时最小值最小。

先考虑第一部分。将所有要求按 \(dep_{u_i}\) 从大到小排序,这是因为如果两条要求发生了冲突,那么留下的一定是 \(u\) 更深的一个,所以先考虑 \(u\) 更深的要求。考虑第 \(i\) 条要求什么时候是没用的。显然如果 \(S_{u_i,k_i}\) 中的某些点已经被原来遍历过的要求覆盖过,那么第 \(i\) 条要求一定无用。由于 \(S_{u_i,k_i}\) 中的点一定深度相同且都为 \(dep_{u_i}+k_i\),所以我们可以将深度相同的点的 dfn 放到一个 vector 中,并给每一个深度建一棵树状数组(注意要用 vector 建,否则会爆空间)。于是我们就可以进行标记和查询了。

(为方便描述,下文将“有用的要求”简称为“要求”)。

再考虑第二部分。做扫描线。从大到小枚举 \(L\),求对于这个 \(L\) 符合条件的最小的 \(R\)。我们维护 \(wh_i\)\(la_i\),分别表示第 \(i\) 个点被哪一个要求覆盖、\(S_{u_i,k_i}\cap [L,n]\) 中编号最小的点的编号(\(wh_i\) 在第一部分中求出)。由贪心可知,每条要求的集合中选 \(la\) 一定最优。用线段树维护哪些点被选、编号最大的被选的点的编号即可(线段树上被选权值为 \(1\),反之为 \(0\))。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,B=500;
vector<int> v[N];
int fa[N],dep[N];
int ll[N],rr[N],dfn[N],cnt,df[N];
vector<int> Nodes[N];
void dfs(int u){
	ll[u]=rr[u]=dfn[u]=++cnt;
	df[cnt]=u;
	dep[u]=dep[fa[u]]+1;
	Nodes[dep[u]].emplace_back(dfn[u]);
	for(auto i:v[u]) dfs(i),rr[u]=rr[i];
}
int n,m;
struct fqr{
	int id,u,k;
}q[N];
int mpos[N<<2],sum[N<<2];
int la[N],wh[N];
void change(int p,int l,int r,int x,int k){
	if(!x) return;
	if(l==r){
		sum[p]+=k;
		if(k==-1) mpos[p]=0;
		else mpos[p]=l;
		return;
	}
	int mid=l+r>>1;
	if(x<=mid) change(p<<1,l,mid,x,k);
	else change(p<<1|1,mid+1,r,x,k);
	mpos[p]=max(mpos[p<<1],mpos[p<<1|1]);
	sum[p]=sum[p<<1]+sum[p<<1|1];
}
map<pair<int,int> ,int> mm;
vector<int> tr[N];
int cmp1(fqr f1,fqr f2){
	return dep[f1.u]>dep[f2.u];
}
int lowbit(int x){
	return x&-x;
}
void add(int p,int x,int k){
	for(;x<=Nodes[p].size();x+=lowbit(x)) tr[p][x]+=k;
}
int ask(int p,int x){
	int ans=0;
	for(;x;x-=lowbit(x)) ans+=tr[p][x];
	return ans;
}
int main(){
//	freopen("lirililarila.in","r",stdin);
//	freopen("lirililarila.out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=2;i<=n;i++) cin>>fa[i],v[fa[i]].emplace_back(i);
	dfs(1);
	cin>>m;
	for(int i=1;i<=m;i++) cin>>q[i].u>>q[i].k,q[i].id=i;
	for(int i=1;i<=n;i++){
		if(!Nodes[i].size()) break;
		sort(Nodes[i].begin(),Nodes[i].end());
		for(int j=1;j<=Nodes[i].size()+10;j++) tr[i].emplace_back(0);
	}
	sort(q+1,q+m+1,cmp1);
	cnt=0;
	for(int i=1;i<=m;i++){
		if(mm.find({q[i].u,q[i].k})!=mm.end()) continue;
		mm[{q[i].u,q[i].k}]=1;
		int de=dep[q[i].u]+q[i].k;
		if(!Nodes[de].size()) continue;
		int l=lower_bound(Nodes[de].begin(),Nodes[de].end(),ll[q[i].u]+1)-Nodes[de].begin()+1,r=upper_bound(Nodes[de].begin(),Nodes[de].end(),rr[q[i].u])-Nodes[de].begin();
		if(l>r) continue;
		if(!(ask(de,r)-ask(de,l-1))){
			cnt++;
			for(int j=l-1;j<r;j++) wh[df[Nodes[de][j]]]=i,add(de,j+1,1);
		}
	}
	int minl=1e9,ansr=0,ansl=0;
	for(int i=n;i>0;i--){
		if(wh[i]){
			change(1,1,n,la[wh[i]],-1);
			change(1,1,n,i,1);
			la[wh[i]]=i;
		}
		if(sum[1]==cnt&&mpos[1]-i+1<=minl) ansl=i,ansr=mpos[1],minl=ansr-ansl+1;
	}
	cout<<ansl<<' '<<ansr;
}
posted @ 2025-05-27 17:20  Fiendish  阅读(75)  评论(0)    收藏  举报