E. Xenia and Tree
题目链接
标签:
LCA,bfs,分块
题意:
给定一颗n个结点的树,先把1号节点染色,其他节点初始状态都是未染色,会给我们m个询问,每个询问有两种类型,“1 x”表示把x结点染色,“2 x”表示求到x点最近的染色的点的距离。
题解:
- 题目中的数据范围是1e5级别,对于询问2可以直接先预处理dis,然后直接进行查询,但是新染色的话需要重新更新所有dis,那么时间复杂度会达到n^2。
- 我们可以进行分块,每个块大小为sqrt(n),如果新染色的点比较少,那么加入vector中,然后查询的时候,只要查询该点到已经更新的点的最小距离和未更新的染色的点的LCA来确定两个点的最小距离,两者取个较小值就是我们要的答案。
- 但vector中的点达到sqrt(n),那么就把vector中的点拿出来进行更新。
- LCA是log级别的,块大小也是log级别,这样时间复杂度达到n*sqrt(m)。
AC代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define endl '\n' const int N=1e5+10;
int n,m,base; int dep[N],fa[N][20],lg[N],dis[N]; vector<int> G[N]; bool vis[N]; inline void init(){ lg[0]=-1,base=sqrt(n)+1,vis[1]=1; for(int i=1;i<N;++i) lg[i]=lg[i>>1]+1; } inline void dfs(int np,int f){ dep[np]=dep[f]+1,fa[np][0]=f,dis[np]=dep[np]-1; for(int i=1;i<=lg[dep[np]]+1;++i) fa[np][i]=fa[fa[np][i-1]][i-1]; for(auto to:G[np]){ if(to!=f) dfs(to,np); } } inline int lca(int u,int v){ if(dep[u]<dep[v]) swap(u,v); while(dep[u]!=dep[v]) u=fa[u][lg[dep[u]-dep[v]]]; if(u==v) return u; for(int i=lg[dep[u]];i>=0;--i){ if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; } return fa[u][0]; } inline int dist(int u,int v){ return dep[u]+dep[v]-2*dep[lca(u,v)]; }
int q[N],cnt=0; queue<int>st; void updata(int u){ vis[u]=1; q[++cnt]=u; st.push(u); dis[u]=0; if(cnt==base){ while(!st.empty()){ int v=st.front(); st.pop(); for(auto x:G[v]){ if(dis[x]>dis[v]+1){ dis[x]=dis[v]+1; st.push(x); } } } cnt=0; } }
int query(int u){ int ans=dis[u]; for(int i=1;i<=cnt;i++)ans=min(ans,dist(q[i],u)); return ans; }
void solve(){ cin>>n>>m; init(); for(int i=1;i<n;i++){ int x,y; cin>>x>>y; G[x].push_back(y),G[y].push_back(x); } dfs(1,0); while(m--){ int x,y; cin>>x>>y; if(x==1)updata(y); else cout<<query(y)<<endl; }
}
signed main(){ #ifdef LOCAL freopen("in.in", "r", stdin); freopen("out.out", "w", stdout); #endif ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; while(t--){ solve(); } return 0; }
|