给出
n
≤
1
e
4
n\leq1e4
n≤1e4的两颗树
T
a
,
T
b
T_a,T_b
Ta,Tb,以及一个
m
≤
1
e
4
m\leq1e4
m≤1e4个点的图
G
G
G,树上的每个结点都代表图
G
G
G上的一条边,一开始图上没有连边。然后要求出对于
∀
i
∈
[
1
,
n
]
\forall i \in [1,n]
∀i∈[1,n],把两颗树各自的根到
i
i
i结点路径上的所有的点上的边加入图中,这个图的连通块的个数。
考虑用可撤销的并查集维护连通块的个数。问题在于遍历到第一个树每个结点
i
i
i的时候,想要维护连通的信息,在第二颗树上必须要遍历整个树,复杂度
O
(
(
n
l
o
g
n
)
2
)
O((nlogn)^2)
O((nlogn)2),效率太低。
一个好的思路是,遍历第二颗树能够多处理一些点。把第一个树按照点的距离
≤
n
\leq\sqrt n
≤n分块,这样的点不超过
n
\sqrt n
n个,把一个块的点信息放在一个点上。
在第一个树上处理的时候,只有遇到关键点,才会遍历第二颗树,而此时同时处理
n
\sqrt n
n个点,第一颗树只有
n
\sqrt n
n个点会去遍历第二颗树。而第二颗树也只有遇到
n
\sqrt n
n个点才会去处理第一颗树的块内信息,块的大小又是小于等于
n
\sqrt n
n的,所以遍历第二颗树的复杂度不会超过
O
(
n
)
O(n)
O(n)。
总复杂度是
O
(
n
n
l
o
g
n
)
O(n\sqrt nlogn)
O(nnlogn)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=1e4+7;
int B;
vector<int> go1[N],go2[N];
struct edge { int u,v; }p1[N],p2[N];
int fa[N],dis[N],c[N],bel[N];
int ans[N];
struct Union {
int f[N],sz[N],stk[N];
int top=0,sum=0;
void init(int n) {
for(int i=1;i<=n;i++)
f[i]=i,sz[i]=1;
sum=n;top=0;
}
int find(int x) {
if(f[x]==x) return x;
else return find(f[x]);
}
void merge(int x,int y) {
x=find(x);y=find(y);
if(x==y) return;
if(sz[x]>sz[y]) swap(x,y);
sum--;f[x]=y;sz[y]+=sz[x];stk[++top]=x;
}
void undo(int s) {
while(top>s) {
int x=stk[top];top--;
sz[f[x]]-=sz[x];
f[x]=x;
sum++;
}
}
}U;
void divide(int u,int f) {
fa[u]=f;
dis[u]=0,c[u]=0;
for(auto &v:go1[u]) {
if(v==f) continue;
divide(v,u);
if(!c[v]) dis[u]=max(dis[u],dis[v]+1);
}
if(dis[u]==B) c[u]=1;
}
int tag;
void dfs2(int u,int f) {
int cur=U.top;
U.merge(p2[u].u,p2[u].v);
for(auto &v:go2[u]) {
if(v==f) continue;
dfs2(v,u);
}
if(bel[u]!=tag) {
U.undo(cur);
return;
}
for(int i=u;i!=tag;i=fa[i])
U.merge(p1[i].u,p1[i].v);
ans[u]=U.sum;
U.undo(cur);
}
void dfs1(int u,int f) {
int cur=U.top;
U.merge(p1[u].u,p1[u].v);
for(auto &v:go1[u]) {
if(v==f) continue;
dfs1(v,u);
}
if(!c[u]) { U.undo(cur); return; }
tag=u;
dfs2(1,0);
U.undo(cur);
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
int n,m;
scanf("%d%d",&n,&m);
memset(go1,0,sizeof(go1));
memset(go2,0,sizeof(go2));
U.init(m);
for(int i=1;i<=n;i++)
scanf("%d%d",&p1[i].u,&p1[i].v);
for(int i=1;i<n;i++) {
int u,v;
scanf("%d%d",&u,&v);
go1[u].push_back(v);
go1[v].push_back(u);
}
for(int i=1;i<=n;i++)
scanf("%d%d",&p2[i].u,&p2[i].v);
for(int i=1;i<n;i++) {
int u,v;
scanf("%d%d",&u,&v);
go2[u].push_back(v);
go2[v].push_back(u);
}
B=sqrt(n);
divide(1,0);
c[1]=1;
for(int j,i=1;i<=n;i++) {
for(j=i;c[j]!=1;j=fa[j]);
bel[i]=j;
}
dfs1(1,0);
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);
}
return 0;
}