CF 1062E Company题解

说在前面

这篇题解中用到的数据结构并非最优 可依照思路用其他数据结构完成(比如\(ST\)表)

题目描述

题目传送门~
给定一颗树,有若干个询问,每个询问给出 \(l\)\(r\),要求编号为 \(l\)~\(r\) 的点任意删去一个之后剩余点的 \(LCA\) 深度最大,输出删去点的编号和 \(LCA\) 的最大深度

算法分析

一个结论:求若干个点的\(LCA\)等同于求其\(dfn\)或者欧拉序最小和最大的\(LCA\)
我们可以用一个\(RMQ\)数据结构维护编号的区间欧拉序最小和最大的节点,这样就能轻松解决区间\(LCA\)啦!
可是删除一个点又该怎么处理?非常明显,根据上面的结论,无论我们删去除欧拉序最大最小的其他哪个点,区间\(LCA\)依旧是不变的,为了删去一个点让\(LCA\)深度变大,我们从欧拉序最大的和最小的里面选出一个删去就可以实现,两个删去后分别求\(LCA\),选择最低的便是答案

Code

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

#define PII pair<int, int>
const int N = 1e5+21;
typedef struct{
	int to;
	int nxt;
}Edge;
Edge edge[N*2];
int head[N];
int cnt;
inline void add_edge(int u, int v){
	edge[cnt].to = v;
	edge[cnt].nxt = head[u];
	head[u] = cnt++;
	return;
}
int tot;
int idx[N];
int dep[N*2];
int eu[N*2];
inline void dfs(int x, int fa, int deep){
	idx[x] = ++tot;
	eu[tot] = x;
	dep[tot] = deep;
	for(int i = head[x]; ~i; i = edge[i].nxt)
		if(edge[i].to != fa){
			dfs(edge[i].to, x, deep+1);
			eu[++tot] = x;
			dep[tot] = deep;
		}
}
int tree[4][N*5];
inline int lchild(int x){return x<<1;}
inline int rchild(int x){return x<<1|1;}
inline void push_up(int x){
	if(idx[tree[2][lchild(x)]] > idx[tree[2][rchild(x)]])
		tree[2][x] = tree[2][rchild(x)];
	else
		tree[2][x] = tree[2][lchild(x)];
	if(idx[tree[3][lchild(x)]] < idx[tree[3][rchild(x)]])
		tree[3][x] = tree[3][rchild(x)];
	else
		tree[3][x] = tree[3][lchild(x)];
}
inline void build1(int p, int pl, int pr){
	if(pl == pr){
		tree[2][p] = tree[3][p] = pl;
		return;
	}
	int mid = (pl+pr) >> 1;
	build1(lchild(p), pl, mid);
	build1(rchild(p), mid+1, pr);
	push_up(p);
}
inline PII query1(int p, int pl, int pr, int l, int r){
	if(pl >= l && pr <= r)
		return make_pair(tree[2][p], tree[3][p]);
	int mid = (pl+pr) >> 1;
	int minn = 0, maxx = 0;
	if(mid >= l){
		PII x = query1(lchild(p), pl, mid, l, r);
		if(!minn)
			minn = x.first;
		else if(idx[minn] > idx[x.first])
				minn = x.first;
		if(!maxx)
			maxx = x.second;
		else if(idx[maxx] < idx[x.second])
			maxx = x.second;
	}
	if(mid < r){
		PII x = query1(rchild(p), mid+1, pr, l, r);
		if(!minn)
			minn = x.first;
		else if(idx[minn] > idx[x.first])
				minn = x.first;
		if(!maxx)
			maxx = x.second;
		else if(idx[maxx] < idx[x.second])
			maxx = x.second;
	}
	return make_pair(minn, maxx);
}
int lg[N*2];
int dp[N*2][22];
inline void ST(){
	for(int i = 1; i <= tot; i++)
		lg[i] = lg[i-1] + (1<<lg[i-1] == i);
	for(int i = 1; i <= tot; i++)
		dp[i][0] = i;
	for(int i = 1; (1<<i) <= tot; i++)
		for(int j = 1; j + (1<<i) - 1 <= tot; j++){
			int a = dp[j][i-1];
			int b = dp[j+(1<<(i-1))][i-1];
			dp[j][i] = dep[a] > dep[b] ? b : a;
		}
	return;
}
inline int LCA(int x, int y){
	int l = idx[x];
	int r = idx[y];
	if(r < l)
		swap(l, r);
	int len = lg[r-l+1]-1;
	int a = dp[l][len];
	int b = dp[r-(1<<len)+1][len];
	return dep[a] > dep[b] ? eu[b] : eu[a];
}
int n, q;
inline int Seg_LCA_Del(int l, int r, int x){
	PII X;
	int a = 0, b = 0;
	if(l <= x-1){
		X = query1(1, 1, n, l, x-1);
		a = LCA(X.first, X.second);
	}
	if(x+1 <= r){
		X = query1(1, 1, n, x+1, r);
		b = LCA(X.first, X.second);
	}
	if(!a)
		return b;
	if(!b)
		return a;
	return LCA(a, b);
}
int main(){
	memset(head, -1, sizeof(head));
	scanf("%d%d", &n, &q);
	for(int i = 2; i <= n; i++){
		int x;
		scanf("%d", &x);
		add_edge(i, x);
		add_edge(x, i);
	}
	dfs(1, 0, 1);
	build1(1, 1, n);
	ST();
	while(q--){
		int l, r;
		scanf("%d%d", &l, &r);
		PII p = query1(1, 1, n, l, r);
		int x = p.first;
		int y = p.second;
		int a = Seg_LCA_Del(l, r, x);
		int b = Seg_LCA_Del(l, r, y);
		if(dep[idx[a]] > dep[idx[b]] || !b)
			printf("%d %d\n", x, dep[idx[a]]-1);
		else if(dep[idx[a]] <= dep[idx[b]] || !a)
			printf("%d %d\n", y, dep[idx[b]]-1);
	}
	return 0;
}
posted @ 2024-05-28 11:24  HurryCine  阅读(8)  评论(0)    收藏  举报