Query on a tree V SPOJ - QTREE5

题意

一棵树,每个节点有黑白颜色之分,q个操作,每次可以改变节点颜色或给定节点v,查询距离v最近的白色节点的距离。

思路

树分治建立重心树,这里用到一个重心树的性质,两个节点a,b在重心树中的LCA节点 f 在a到b的路径上,因为由重心树的定义可知删去f后a,b必然在两个不同连通分量内。由此可以对最短路径ans进行更新,设v为查询点,g为其祖先节点,op(v)为以v为根的子树中v到白色节点的最小距离,对其所有祖先节点进行更新,因为重心树的深度是O(log n)的,所以这样就能在O(log n)时间内求得最短距离:
ans=min(ans,dist(g,v)+op(g))
其中dist可以用LCA倍增在O(log n)时间求得,而op(v)的维护可以建立优先队列,元素为距离和点的编号,当将节点变白时,对v的祖先节点都加入新元素,变黑时,将节点标黑,在查询时遇见黑色的就弹出队列top,否则返回top的最小距离。

代码

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<sstream>
#include<cmath>
#include<algorithm>
#include<map>
#include<fstream>
#include<set>
#define mp make_pair
#define pii pair<int,int>
#define pdd pair<double,double>
#define mst(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const double eps=1e-10;
const int maxn=100000+9;
const double pi=3.14159265358979323846264338328;
const int inf=1e9+9;
int mod=int(1e9)+7;
struct edge{
	int to,next;
	edge(){
	}
	edge(int to,int next):to(to),next(next){
	}
}g[maxn*3];
int head[maxn],now[maxn],cnt;
bool cen[maxn];
int subsize[maxn];
void add(int from,int to){
	if(head[from]==-1) head[from]=cnt;
	else g[now[from]].next=cnt;
	now[from]=cnt;
	g[cnt++]=edge(to,-1);
}
int cal(int u,int p){
	int c=1;
	for(int i=head[u];i>=0;i=g[i].next){
		if(g[i].to==p||cen[g[i].to]) continue;
		c+=cal(g[i].to,u); 
	}
	return subsize[u]=c;
}
int ser(int u,int p,int t){
	for(int i=head[u];i>=0;i=g[i].next){
		int v=g[i].to;
		if(v==p||cen[v]) continue;
		if(subsize[v]>t/2) return ser(v,u,t);
	}
	return u;
}
int dad[maxn];
void hj(int u,int p){
	cal(u,p);
	int s=ser(u,p,subsize[u]);
	cen[s]=1;
	dad[s]=p;
	if(p==-1) dad[s]=s;
	for(int i=head[s];i>=0;i=g[i].next){
		int v=g[i].to;
		if(v==p||cen[v]) continue;
		hj(v,s);
	}
}
int ans;
int depth[maxn],vis[maxn],parent[20][maxn],dist[20][maxn];
void dfs(int u,int p,int d){
	parent[0][u]=p;
	dist[0][u]=1;depth[u]=d;
	if(p==-1) dist[0][u]=-1;
	for(int i=head[u];i>=0;i=g[i].next){
		if(g[i].to!=p) dfs(g[i].to,u,d+1);
	}
}
void init(int n){
	dfs(1,-1,0);
	for(int i=0;i<19;++i){
		for(int j=1;j<=n;++j){
			if(parent[i][j]==-1){
				parent[i+1][j]=dist[i+1][j]=-1;
				continue;
			}
			parent[i+1][j]=parent[i][parent[i][j]];
			if(dist[i][parent[i][j]]>=0) dist[i+1][j]=dist[i][j]+dist[i][parent[i][j]]; 
		}
	}
}
int lca(int u,int v){
	if(depth[u]>depth[v]) swap(u,v);
	int ans=0;
	for(int i=0;i<20;++i){
		if((depth[v]-depth[u])>>i&1){
			ans+=dist[i][v];
			v=parent[i][v];
		}
	}
	if(u==v) return ans;
	for(int i=19;i>=0;--i){
		if(parent[i][u]!=parent[i][v]){
			ans+=dist[i][u]+dist[i][v];
			v=parent[i][v];
			u=parent[i][u];
		}
	}
	ans+=dist[0][u]+dist[0][v];
	return ans;
}
struct node{
	int to,dist;
	node(){
	}
	node(int dist,int to):dist(dist),to(to){
	} 
	bool operator <(const node &p)const{
		return dist>p.dist;
	}
};
priority_queue<node> op[maxn];
int sol(int u){
	while(!op[u].empty()){
		node p=op[u].top();
		if(!vis[p.to]){
			op[u].pop();
		}else return p.dist;
	}
	return inf;
}
int main()
{
	int n;
	cin>>n;
	cnt=0;
	mst(head,-1);
	for(int i=0;i<n-1;++i){
		int a,b;
		scanf("%d%d",&a,&b);
		add(a,b);add(b,a);
	}
	init(n);
	hj(1,-1);
	int q;
	cin>>q;
	ans=0;
	while(q--){
		int opt,v;
		scanf("%d%d",&opt,&v);
		if(opt){
			if(vis[v]) printf("0\n");
			else if(!ans) printf("-1\n");
			else{
				int tmp=inf;
				for(int i=v;;i=dad[i]){
					tmp=min(tmp,sol(i)+lca(i,v));
					if(dad[i]==i) break;
				}
				printf("%d\n",tmp);
			}
		}else{
			vis[v]=!vis[v];
			if(vis[v]){
				++ans;
				for(int i=v;;i=dad[i]){
					op[i].push(node(lca(i,v),v));
					if(dad[i]==i) break;
				}
			}else --ans;
		}
	}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值