342E. Xenia and Tree(sqrt-optimization+多源点BFS+倍增LCA)

给出一棵树,一开始1号点是红色的,有2种操作:

(1)把一个节点改成红色

(2)每次询问一个红色节点,输出和它最接近的红色节点

一个全新的套路:对询问分块+多源(BFS)

用一个(Vector)保存当前还未被修改的红色节点

将修改操作分块后,对每一块的操作先全部修改一遍,然后多源(BFS)更新每个蓝色节点和距离它最近红色节点的距离。

然后对每个询问,先确定它距离已经修改的红色节点的最近距离,然后枚举还没修改的红色节点,用倍增(LCA)查询距离并更新之前保存的最近距离。

因为分块的存在,可以保证(Vector)的大小始终是(sqrt n)

总体时间复杂度(nsqrt nlogn)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
int n,m;
vector<int> g[maxn];
int is[maxn];//表示是否是红色节点
int h[maxn],father[30][maxn];
int d[maxn];//表示距离当前最近红色节点的距离
vector<int> v;//表示还没有被染色的红色节点
void dfs (int x) {
	for (int y:g[x]) {
		if (y==father[0][x]) continue;
		father[0][y]=x;
		h[y]=h[x]+1;
		dfs(y);
	}
}
int lca (int x,int y) {
	if (h[x]<h[y]) swap(x,y);
	for (int i=20;i>=0;i--) if (h[x]-h[y]>>i) x=father[i][x];
	if (x==y) return x;
	for (int i=20;i>=0;i--) {
		if (father[i][x]!=father[i][y]) {
			x=father[i][x];
			y=father[i][y];
		}
	}
	return father[0][x];
}
int getDis (int x,int y) {
	return h[x]+h[y]-2*h[lca(x,y)];
}
int vis[maxn];
void bfs () {
	queue<int> q;
	for (int i=1;i<=n;i++) vis[i]=0;
	for (int i=1;i<=n;i++) if (is[i]) q.push(i),vis[i]=1,d[i]=0;
	while (q.size()) {
		int x=q.front();
		q.pop();
		for (int y:g[x]) {
			if (vis[y]) continue;
			d[y]=d[x]+1;
			vis[y]=1;
			q.push(y);
		}
	}
}
int main () {
	scanf("%d%d",&n,&m);
	for (int i=1;i<n;i++) {
		int x,y;
		scanf("%d%d",&x,&y);
		g[x].push_back(y);
		g[y].push_back(x);
	}
	is[1]=1;
	dfs(1);
	bfs();
	for (int i=1;i<=20;i++) for (int j=1;j<=n;j++) father[i][j]=father[i-1][father[i-1][j]];
	
	int Size=pow(4.5*m*log(n)/log(2.0),1.0/3)+1;
	
	
	for (int i=1;i<=m;i++) {
		int x,y;
		scanf("%d%d",&x,&y);
		if (x==1) {
			v.push_back(y);
			int vs=v.size();
			if (vs==Size) {
				//一个块满
				for (int j=0;j<v.size();j++) is[v[j]]=1;
				bfs();
				v.clear(); 
			}
		}
		else {
			int ans=d[y];
			for (int j=0;j<v.size();j++) ans=min(ans,getDis(y,v[j]));
			printf("%d
",ans);
		}
	}
}
原文地址:https://www.cnblogs.com/zhanglichen/p/14371379.html