大家好,我是毒瘤,喜歡用玄學演算法過題。
發現題解區沒有這個做法,於是來發一篇。
思路
不難發現如果一個點對 \((u,v)\) 的距離為 \(d\),那麼在這棵樹以 \(u\) 為根時,\(v\) 的深度為 \(d\)。於是考慮換根 DP。
首先思考如何計算答案。顯然我們可以將查詢離線下來,然後當換根到以 \(u\) 為根時,將有關 \(u\) 的所有查詢全部解決即可。
其次,發現當一個 \(v\) 轉化成根時,它會在其父節點 \(u\) 為根時的形態的基礎上,所在 \(v\) 子樹中的節點深度減 \(1\),其餘節點加 \(1\)。
然而,我們需要求的是深度為 \(d\) 的節點,於是單單想大多數的換根 DP 維護深度的極值是不行的,所以需要更新出所有的深度。
於是這個東西需要使用資料結構維護。明確這個資料結構所需的功能:
- 區間加減。
- 區間查詢權值為 \(k\) 的編號。
於是你就想到了這道題。即用分塊實現,一個 vector
維護塊內的元素,每一次每一次修改都需要一次排序,查詢都需要一次二分,單次操作是 \(\Theta(\sqrt n \log \sqrt n)\) 的。配合上巨大常數,你得到了 TLE 15 的程式碼。
然後你可以發現此題是維護深度,所以值域很小,所以可以開桶維護。你在查詢的時候每次都先看一下在這塊中有沒有出現 \(k\),如果出現了就直接暴力找;否則就繼續向前面的塊判斷。這樣實現即可單次操作做到 \(\Theta(\sqrt n)\)。
於是你就得到了理論時間複雜度為 \(\Theta(q \sqrt n)\) 的程式碼,但是常數有點小大,但是發現每一次的桶都不需要清空完,只需將原本的 \(val\) 刪掉,然後加上新的 \(val\) 即可。
Code
#include <bits/stdc++.h>
#define fst first
#define snd second
#define re register
using namespace std;
typedef pair<int,int> pii;
const int N = 2e5 + 10,M = 4e5 + 10,K = 1010;
int n,q;
int idx,h[N],ne[M],e[M],ans[N];
int num,id[N],pid[N],sz[N],d[N],val[N];
vector<pii> Q[N];
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
inline void add(int a,int b){
ne[idx] = h[a];
e[idx] = b;
h[a] = idx++;
}
struct block{
int len,num;
int pos[N],L[K],R[K],tag[K],vis[K][N];
inline void init(){
len = num = sqrt(n);
for (re int i = 1;i <= n;i++) val[id[i]] = d[i];
for (re int i = 1;i <= num;i++){
L[i] = (i - 1) * len + 1;
R[i] = i * len;
}
if (R[num] != n){
num++;
L[num] = (num - 1) * len + 1;
R[num] = n;
}
for (re int i = 1;i <= num;i++){
for (re int j = L[i];j <= R[i];j++){
pos[j] = i;
vis[i][val[j]]++;
}
}
}
inline void modify(int l,int r,int k){
int p = pos[l],q = pos[r];
if (p == q){
for (re int i = l;i <= r;i++){
vis[p][val[i]]--;
val[i] += k;
vis[p][val[i]]++;
}
}
else{
for (re int i = l;i <= R[p];i++){
vis[p][val[i]]--;
val[i] += k;
vis[p][val[i]]++;
}
for (re int i = p + 1;i < q;i++) tag[i] += k;
for (re int i = L[q];i <= r;i++){
vis[q][val[i]]--;
val[i] += k;
vis[q][val[i]]++;
}
}
}
inline int query(int l,int r,int k){
int p = pos[l],q = pos[r];
if (p == q){
for (re int i = l;i <= r;i++){
if (val[i] + tag[p] == k) return pid[i];
}
}
else{
for (re int i = l;i <= R[p];i++){
if (val[i] + tag[p] == k) return pid[i];
}
for (re int i = p + 1;i < q;i++){
if (vis[i][k - tag[i]]){
for (re int j = L[i];j <= R[i];j++){
if (val[j] + tag[i] == k) return pid[j];
}
}
}
for (re int i = L[q];i <= r;i++){
if (val[i] + tag[q] == k) return pid[i];
}
}
return -1;
}
}bl;
inline void calc(int u){
for (auto x:Q[u]) ans[x.snd] = bl.query(1,n,x.fst + 1);
}
inline void dfs1(int u,int fa){
num++;
sz[u] = 1;
id[u] = num;
pid[num] = u;
d[u] = d[fa] + 1;
for (re int i = h[u];~i;i = ne[i]){
int j = e[i];
if (j == fa) continue;
dfs1(j,u);
sz[u] += sz[j];
}
}
inline void dfs2(int u,int fa){
for (re int i = h[u];~i;i = ne[i]){
int j = e[i];
if (j == fa) continue;
bl.modify(1,n,1);
bl.modify(id[j],id[j] + sz[j] - 1,-2);
calc(j);
dfs2(j,u);
bl.modify(1,n,-1);
bl.modify(id[j],id[j] + sz[j] - 1,2);
}
}
int main(){
memset(h,-1,sizeof(h));
n = read();
for (re int i = 1;i < n;i++){
int a,b;
a = read();
b = read();
add(a,b);
add(b,a);
}
q = read();
for (re int i = 1;i <= q;i++){
int a,b;
a = read();
b = read();
Q[a].push_back({b,i});
}
for (re int i = 1;i <= n;i++) sort(Q[i].begin(),Q[i].end());
dfs1(1,0);
bl.init();
calc(1);
dfs2(1,0);
for (re int i = 1;i <= q;i++) printf("%d\n",ans[i]);
return 0;
}