树状数组,倍增,枚举,$dfs$序。
对于每一次的询问,可以枚举$B$集合中的所有点,对于每一个点,在树上二分$LCA$,找到最低的更新答案。
判断是否是$LCA$可以搞个$dfs$序,将$A$集合中所有点标$1$,然后查询子树对应的区间上的区间和。
#include <bits/stdc++.h> using namespace std; const int maxn = 100010; int L[maxn],R[maxn]; int c[2*maxn]; int h[maxn]; int to[2*maxn]; int nx[2*maxn]; int sz; int tt; int dp[100010][25]; int dep[100010]; int ans; int n,m; int k1,k2; int A[100010],B[100010]; void add(int a,int b) { to[sz] = b; nx[sz] = h[a]; h[a] = sz++; } void dfs(int x,int y) { L[x] = tt; tt++; dp[x][0] = y; if(y==-1) dep[x]=1; else dep[x] = dep[y]+1; for(int i=h[x];i!=-1;i = nx[i]) { if(L[to[i]]) continue; dfs(to[i],x); } R[x] = tt; tt++; } int lowbit(int x) { return x&(-x); } void update(int pos,int val) { for(int i=pos;i<=2*n;i=i+lowbit(i)) { c[i] += val; } } int get(int pos) { int res=0; for(int i=pos;i>0;i=i-lowbit(i)) { res=res+c[i]; } return res; } void work(int x) { while(1) { int Q = dp[x][0]; if(get(R[Q])-get(L[Q]-1)) { ans = max(ans,dep[Q]); break; } for(int i=17;i>=0;i--) { int to = dp[x][i]; if(to==-1) continue; if(get(R[to])-get(L[to]-1)) continue; x=to; break; } } x = dp[x][0]; ans = max(ans,dep[x]); } int main() { while(~scanf("%d%d",&n,&m)) { sz=0; for(int i=0;i<=n;i++) { c[i]=0; h[i]=-1; L[i]=0; R[i]=0; } for(int i=1;i<=n-1;i++) { int a,b; scanf("%d%d",&a,&b); add(a,b); add(b,a); } tt=1; dfs(1,-1); for(int j=1;j<=17;j++) { for(int i=1;i<=n;i++) { if(dp[i][j-1]==-1) dp[i][j]=-1; else dp[i][j] = dp[dp[i][j-1]][j-1]; } } for(int i=1;i<=m;i++) { scanf("%d",&k1); for(int j=1;j<=k1;j++) scanf("%d",&A[j]), update(L[A[j]],1); scanf("%d",&k2); ans=0; for(int j=1;j<=k2;j++) { scanf("%d",&B[j]); if(get(R[B[j]])-get(L[B[j]]-1)) { ans = max(ans,dep[B[j]]); continue; } work(B[j]); } printf("%d\n",ans); for(int j=1;j<=k1;j++) update(L[A[j]],-1); } } return 0; }
时间: 2024-10-19 21:14:49