题解:CF1904E Tree Queries

思路:

离线 + 分块。

观察一下性质发现,一个点不能经过等价于将树分成两部分。

最后我们要确定 \(x\) 所在联通块的范围。

对于不能经过的点 \(a_i\),我们先找出在 \(x\)\(1\) 路径上深度最深的 \(a_i\),记为 \(y\),如果没有 \(y\) 默认为 \(1\)。这一步可以树上倍增来求。

\(x\) 所在联通块的就是 \(y\) 这棵子树的子集。

对于剩下的 \(a_i\),如果 \(a_i\)\(y\) 这棵子树内,那么 \(a_i\) 这棵子树不可达,无效。

把树拍平,按照 dfs 序对应到区间上,\(y\) 这棵子树对应的区间 \([l,r]\)\(y\) 子树内不可达的 \(a_i\) 对应的子树区间为 \([l_i,r_i]\),设这些区间的并集为 \(S\),那么我们就是要询问以 \(x\) 为根时,对应的区间集合 \([l,r]\setminus S\),即当 \([l,r]\) 是全集的时候,\(S\) 的补集中,到 \(x\) 距离最远的点。

那么如何求到 \(x\) 最远的点?

考虑先求出以 \(1\) 为根时所有点的距离,利用换根思想,对于根从 \(u\rightarrow v\) 时,\(v\) 这棵子树内的点到 \(v\) 的比在原来到 \(u\) 的基础上减少 \(1\),这一步就是子树加减 \(1\) 的操作,而对于其他点,到 \(v\) 的距离比原来到 \(u\) 的距离增加了 \(1\),修改时直接全局加 \(1\) ,子树减 \(2\) 就行了。

时间复杂度为 \(O(n\log n+n\sqrt n)\),如果用其他数据结构例如线段树可以 \(O(n\log n)\)

code

const int N=2e5+10,M=450;
struct Query{
	int l,r,id;
	vector<pair<int,int>>Seg;
};
vector<Query>S[N];
int Be[N],L[M],R[M],ma[M],add[M];
int n,m,dep[N],rt=1;
int dfn[N],ID[N],siz[N],t=0;
int fa[N][21],Ans[N];
vector<int>G[N];
void dfs(int now,int from)
{
	ID[dfn[now]=++t]=now;siz[now]=1;	
	for(int i=1;(1<<i)<=dep[now];i++)
		fa[now][i]=fa[fa[now][i-1]][i-1];
	for(int to:G[now])
	{
		if(to==from) continue;
		dep[to]=dep[now]+1,fa[to][0]=now;
		dfs(to,now);
		siz[now]+=siz[to];
	}
	return;
}
int LCA(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;~i;i--)
		if((1<<i)<=dep[x]-dep[y]) x=fa[x][i];
	if(x==y) return x;
	for(int i=20;~i;i--)
		if(fa[x][i]^fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
#define fi(x) x.first
#define se(x) x.second
void build()
{
	int len=sqrt(n),t=n/len+bool(n%len);
	for(int i=1;i<=n;i++)
	{
		Be[i]=(i-1)/len+1;
		if(!L[Be[i]]) L[Be[i]]=i;
		R[Be[i]]=i;
	}
	for(int i=1;i<=t;i++)
		for(int j=L[i];j<=R[i];j++) ma[i]=max(ma[i],dep[ID[j]]);
	return;
}
inline int query(int l,int r)
{
	if(l>r) return -1;
	int res=0;
	if(Be[l]==Be[r]){
		for(int i=l;i<=r;i++)
			res=max(res,dep[ID[i]]+add[Be[l]]);
	}
	else{
		for(int i=l;i<=R[Be[l]];i++) res=max(res,dep[ID[i]]+add[Be[l]]);
		for(int i=L[Be[r]];i<=r;i++) res=max(res,dep[ID[i]]+add[Be[r]]);
		for(int i=Be[l]+1;i<=Be[r]-1;i++) res=max(res,ma[i]+add[i]);
	}
	return res;
}
inline void rebuild(int now)
{
	ma[now]=0;
	for(int i=L[now];i<=R[now];i++) 
		ma[now]=max(ma[now],dep[ID[i]]);
	return;
}
inline void modify(int l,int r,int val)
{
	if(Be[l]==Be[r]){
		for(int i=l;i<=r;i++)
			dep[ID[i]]+=val;
		rebuild(Be[l]);
	}
	else{
		for(int i=l;i<=R[Be[l]];i++) dep[ID[i]]+=val;
		for(int i=L[Be[r]];i<=r;i++) dep[ID[i]]+=val;
		rebuild(Be[l]);rebuild(Be[r]);
		for(int i=Be[l]+1;i<=Be[r]-1;i++) add[i]+=val;
	}
	return;
}
void calc(int now,int from)
{
	for(auto [l,r,id,Seg]:S[now])
	{
		int res=0;
		if(!Seg.size()) res=max(res,query(l,r));
        //注意特判 y 子树都合法的情况
		else{
			sort(Seg.begin(),Seg.end());
			int up=Seg.size()-1;
			int mi=n+1,ma=-1;
			for(auto [pl,pr]:Seg) mi=min(mi,pl),ma=max(ma,pr);
			if(mi>l) res=max(res,query(l,mi-1));
			if(ma<r) res=max(res,query(ma+1,r));
            //两端可能还有合法区间
			ma=se(Seg[0]);
			for(int i=1;i<=up;i++)
			{
				if(fi(Seg[i])<=ma) continue;
				res=max(res,query(ma+1,fi(Seg[i])-1));
				ma=se(Seg[i]);
			}
            //询问补集
		}
		Ans[id]=res;
	}
	for(int to:G[now])
	{
		if(to==from) continue;
		modify(dfn[to],dfn[to]+siz[to]-1,-2),modify(1,n,1);//换根
		calc(to,now);
		modify(dfn[to],dfn[to]+siz[to]-1,2),modify(1,n,-1);//记得换回来
	}
}
int main()
{
	read(n,m);
	for(int i=1,x,y;i<n;i++)
	{
		read(x,y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
	dfs(rt,0);build();
	int x,k;
	for(int i=1;i<=m;i++)
	{
		read(x,k);
		vector<int>d;
		int up=dfn[rt],w=rt;
		for(int j=1,y,z;j<=k;j++)
		{
			read(y);d.push_back(y);
			if(LCA(x,y)==y){
				z=x;
				for(int p=20;~p;p--)
					if(fa[z][p]&&dep[fa[z][p]]>dep[y]) z=fa[z][p];
				if(dfn[z]>up) up=dfn[z],w=z;//倍增求 y
			}
		}
		vector<pair<int,int>>c;
		for(int to:d)
		{
			if(to==w) continue;
			if(LCA(to,w)==w)
				c.push_back({dfn[to],dfn[to]+siz[to]-1});
           	//把不可达的子树对应区间抠出来
		}	
		S[x].push_back({dfn[w],dfn[w]+siz[w]-1,i,c});
	}
	calc(rt,0);
	for(int i=1;i<=m;i++) write(Ans[i],'\n');
    return 0;
}
posted @ 2024-11-08 14:48  EityDawn  阅读(30)  评论(0)    收藏  举报