[省选联考 2021 A/B 卷] 宝石
不妨先研究部分分,观察到有链的部分,于是我们可以很容易想到可以维护出一个每一个点的下一个能成功匹配位置的数组,然后为这个数组再维护一个倍增。
很容易地想到可以将这个想法搬迁到树上。但是对于每一个询问的维护的路径是很复杂的,不妨离线下来点分治。
考虑点分治的时候处理两种倍增数组,维护成功匹配若干个的位置。
然后再 dp 下去,可以在 dp 到某个点的时候计算出它往上到根节点,以每种位置为终点最多可以往前匹配多少步,然后我们就可以在终点处二分统计答案。
复杂度\(O(n (\log_{2}^n)^2 + q \log_{2}^n)\)。
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define pii pair <int , int>
#define mp make_pair
#define fs first
#define sc second
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s^'0');s=getchar();}
x *= f;
}
template <typename T>
void write(T x , char s='\n') {
if(!x) {putchar('0');putchar(s);return;}
if(x<0) {putchar('-');putchar(s);return;}
int tmp[25] = {} , t = 0;
while(x) tmp[t ++] = x % 10 , x /= 10;
while(t -- > 0) putchar(tmp[t] + '0');
putchar(s);
}
const int MAXN = 2e5 + 5;
int n , m , c;
int P[MAXN] , pos[MAXN] , w[MAXN];
int head[MAXN] , to[MAXN << 1] , nxt[MAXN << 1] , cnt;
void add(int u , int v) {nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;}
int ans[MAXN] , Q;
vector <pii> q[MAXN];
int siz[MAXN] , rt , mx , vis[MAXN];
void get_siz(int x , int fa) {
siz[x] = 1;
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if(v == fa || vis[v]) continue;
get_siz(v , x);
siz[x] += siz[v];
}
}
void get_root(int x , int fa , int num) {
int cur = 0;
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if(v == fa || vis[v]) continue;
get_root(v , x , num);
if(siz[v] > cur) cur = siz[v];
}
if(num - siz[x] > cur) cur = num - siz[x];
if(cur < mx) mx = cur , rt = x;
}
int cop[MAXN] , id[MAXN];
int f1[MAXN][22] , f2[MAXN][22] , dp[MAXN];
void dfs(int x , int fa , int I) {
id[x] = I;
int tmp = cop[pos[w[x]]];
if(pos[w[x]]) {
cop[pos[w[x]]] = x;
f2[x][0] = cop[pos[w[x]] + 1];
for (int i = 1; i <= 20; ++i) f2[x][i] = f2[f2[x][i - 1]][i - 1];
}
f1[x][0] = cop[1];
for (int i = 1; i <= 20; ++i) f1[x][i] = f2[f1[x][i - 1]][i - 1];
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if(v == fa || vis[v]) continue;
dfs(v , x , I);
}
cop[pos[w[x]]] = tmp;
}
int get_val(int x) {
int res = 0;
for (int i = 20; i >= 0; --i) {
if(f1[x][i]) {
res += (1 << i);
x = f1[x][i];
break;
}
}
if(!res) return res;
for (int i = 20; i >= 0; --i) {
if(f2[x][i]) {
res += (1 << i);
x = f2[x][i];
}
}
return res;
}
void cal(int x , int fa) {
int tmp = dp[pos[w[x]]];
if(pos[w[x]] && !vis[x]) dp[pos[w[x]]] = max(dp[pos[w[x]]] , dp[pos[w[x]] - 1] + 1);
for (int i = 0; i < (int)q[x].size(); ++i) {
int s = q[x][i].fs , p = q[x][i].sc;
if(id[s] == id[x] || !id[s] || !id[x]) continue;
ans[p] = get_val(s);
int cur = ans[p];
int l = cur , r = c;
while(l <= r) {
int mid = (l + r) >> 1;
if(dp[mid] + cur >= mid) l = mid + 1 , ans[p] = mid;
else r = mid - 1;
}
}
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if(v == fa || vis[v]) continue;
cal(v , x);
}
dp[pos[w[x]]] = tmp;
}
void clr(int x , int fa) {
id[x] = cop[pos[w[x]]] = dp[pos[w[x]]] = 0;
for (int i = 0; i <= 20; ++i) f1[x][i] = f2[x][i] = 0;
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if(v == fa || vis[v]) continue;
clr(v , x);
}
}
void work(int x) {
get_siz(x , 0);
rt = mx = 1e9;
get_root(x , 0 , siz[x]);
x = rt;
vis[x] = 1;
cop[pos[w[x]]] = x;
int num = 0;
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if(vis[v]) continue;
dfs(v , x , ++num);
}
id[x] = ++num;
cal(x , x);
clr(x , x);
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if(vis[v]) continue;
work(v);
}
}
int main() {
freopen("gem.in" , "r" , stdin);
freopen("gem.out" , "w" , stdout);
read(n),read(m),read(c);
for (int i = 1; i <= c; ++i) read(P[i]) , pos[P[i]] = i;
for (int i = 1; i <= n; ++i) read(w[i]);
for (int i = 1; i < n; ++i) {
int u , v;
read(u),read(v);
add(u , v) , add(v , u);
}
read(Q);
for (int i = 1; i <= Q; ++i) {
int s , t;
read(s),read(t);
q[t].push_back(mp(s , i));
}
work(1);
for (int i = 1; i <= Q; ++i) write(ans[i]);
return 0;
}