[題解]AT_abc267_f [ABC267F] Exactly K Steps

WaterSunHB發表於2024-06-22

大家好,我是毒瘤,喜歡用玄學演算法過題。

發現題解區沒有這個做法,於是來發一篇。

思路

不難發現如果一個點對 \((u,v)\) 的距離為 \(d\),那麼在這棵樹以 \(u\) 為根時,\(v\) 的深度為 \(d\)。於是考慮換根 DP。

首先思考如何計算答案。顯然我們可以將查詢離線下來,然後當換根到以 \(u\) 為根時,將有關 \(u\) 的所有查詢全部解決即可。

其次,發現當一個 \(v\) 轉化成根時,它會在其父節點 \(u\) 為根時的形態的基礎上,所在 \(v\) 子樹中的節點深度減 \(1\),其餘節點加 \(1\)

然而,我們需要求的是深度為 \(d\) 的節點,於是單單想大多數的換根 DP 維護深度的極值是不行的,所以需要更新出所有的深度。

於是這個東西需要使用資料結構維護。明確這個資料結構所需的功能:

  1. 區間加減。
  2. 區間查詢權值為 \(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;  
}  

相關文章