题解:P12698 [KOI 2022 Round 2] 树与查询
原问题的询问中,点构成若干个连通块,每个连通块中的点都相互可达。因此对于一个大小为
一个直接的想法是用 DFS 求给定的点中构成了多少个连通块并统计每个连通块的大小,但由于对某个点的 DFS 要遍历其全部出边,最坏情况下时间复杂度是
考虑均摊每条边,对于一棵树,除了根节点以外,每个节点都有且仅有一条连向父节点的边。因此对每个点定它的边就是连接它和父节点的边。
对于每个询问,如果给定的点集中出现了有父子关系的两点,那么它们之间就存在一条边,可以用并查集维护连边的关系。最后统计并查集中每个连通块的大小即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define up(l,r,i) for(int i=(l);i<=(r);++i)
ll n,q,k,a[250007],ans,f[250007],fa[250007];
vector<int> G[250007];
map<ll,ll> cnt;
map<ll,bool> vis;
ll getfa(ll u){
return fa[u]==u?u:fa[u]=getfa(fa[u]);
}
void dfs(int u,int _f){
f[u]=_f;
for(int v:G[u]){
if(v==_f)continue;
dfs(v,u);
}
}
int main(){
cin>>n;
up(2,n,i){
ll u,v;cin>>u>>v;
G[u].push_back(v);G[v].push_back(u);
}
dfs(1,0);
cin>>q;
while(q--){
vis.clear();cnt.clear();ans=0;
cin>>k;up(1,k,i)cin>>a[i],vis[a[i]]=1,fa[a[i]]=a[i];
up(1,k,i){
if(vis[a[i]]&&vis[f[a[i]]]){
ll u=a[i],v=f[a[i]];
u=getfa(u),v=getfa(v);
fa[u]=v;
}
}
up(1,k,i)++cnt[getfa(a[i])];
for(auto tmp:cnt){
ll qwq=tmp.second;
ans+=(qwq-1)*(qwq)/2;
}
cout<<ans<<'\n';
}
}