题意
一棵树,每个节点有黑白颜色之分,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;
}