题目链接 2017 CCPC Hangzhou Problem H
思路:对树进行分块。把第一棵树分成$\sqrt{n}$块,第二棵树也分成$\sqrt{n}$块。
分块的时候满足每个块是一个连通块,那么每个块就有一个共同的祖先。
把询问按照第一个点被第一棵树的哪个祖先管辖和第二个点被第二棵树的哪个祖先管辖,分成$n$类。
每一类询问一起处理,处理完后用可撤销并查集恢复到之前的状态。
每一类询问之间依次转移,每次转移,移动次数不会超过$\sqrt{n}$次。
最后总时间复杂度$O(n^{1.5}logn)$
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cassert>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
const int N = 1e4 + 10;
const int M = 505;
struct node{
int x, y, id;
} s[N << 5];
int T;
int n, m;
int top;
int fa1[N], fa2[N], bs;
int id1[N], id2[N];
int re[M][M];
int father[N], sz[N];
int ans;
int ret[N];
int ux[N], uy[N], vx[N], vy[N];
vector <int> v[N], v1, v2;
vector <node> q[M][M];
void dfs1(int x, int fa, int dep){
fa1[x] = fa;
id1[x] = -1;
if (dep % bs == 0){
id1[x] = v1.size();
v1.push_back(x);
}
for (auto u : v[x]){
if (u == fa) continue;
dfs1(u, x, dep + 1);
}
}
void dfs2(int x, int fa, int dep){
fa2[x] = fa;
id2[x] = -1;
if (dep % bs == 0){
id2[x] = v2.size();
v2.push_back(x);
}
for (auto u : v[x]){
if (u == fa) continue;
dfs2(u, x, dep + 1);
}
}
void merge_(int x, int y){
while (x != father[x]) x = father[x];
while (y != father[y]) y = father[y];
if (x == y) return;
if (sz[x] > sz[y]) swap(x, y);
s[++top] = {x, y};
father[x] = y;
--ans;
sz[y] += sz[x];
}
void recover(int x, int y){
while (top > re[id1[x]][id2[y]]){
auto u = s[top--];
++ans;
father[u.x] = u.x;
father[u.y] = u.y;
sz[u.y] -= sz[u.x];
}
}
void commit(int x, int y){
int tx = x, ty = y;
for (; id1[tx] == -1; tx = fa1[tx]){}
for (; id2[ty] == -1; ty = fa2[ty]){}
recover(tx, ty);
while (x != tx){
merge_(ux[x], vx[x]);
x = fa1[x];
}
while (y != ty){
merge_(uy[y], vy[y]);
y = fa2[y];
}
}
int main(){
scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
rep(i, 1, n) scanf("%d%d", ux + i, vx + i);
bs = sqrt(n);
rep(i, 0, n + 1) v[i].clear();
rep(i, 2, n){
int x, y;
scanf("%d%d", &x, &y);
v[x].push_back(y);
v[y].push_back(x);
}
v1.clear();
dfs1(1, 0, 0);
rep(i, 0, n + 1) v[i].clear();
rep(i, 1, n) scanf("%d%d", uy + i, vy + i);
rep(i, 2, n){
int x, y;
scanf("%d%d", &x, &y);
v[x].push_back(y);
v[y].push_back(x);
}
v2.clear();
dfs2(1, 0, 0);
rep(i, 1, n){
int u, v, x, y;
u = v = i;
x = u, y = v;
for (; id1[x] == -1; x = fa1[x]){}
for (; id2[y] == -1; y = fa2[y]){}
q[id1[x]][id2[y]].push_back({u, v, i});
}
rep(i, 1, m) father[i] = i, sz[i] = 1;
ans = m;
merge_(ux[1], vx[1]);
merge_(uy[1], vy[1]);
re[0][0] = (top = 0);
for (auto x : v1){
for (auto y : v2){
if (x == 1 && y == 1){
merge_(ux[x], vx[x]);
merge_(uy[y], vy[y]);
}
else if (y == 1){
commit(fa1[x], y);
merge_(ux[x], vx[x]);
}
else{
commit(x, fa2[y]);
merge_(uy[y], vy[y]);
}
re[id1[x]][id2[y]] = top;
for (auto u : q[id1[x]][id2[y]]){
commit(u.x, u.y);
ret[u.id] = ans;
}
q[id1[x]][id2[y]].clear();
}
}
rep(i, 1, n) printf("%d\n", ret[i]);
}
return 0;
}