保护

保护

给出一棵树,若干条树上路径。若干次询问某一点到根的路径上,被覆盖次数大于等于k的点的最浅深度。

把lca标记打到x点和y点的深度线段树上,那么从下往上线段树合并即可。当然,用主席树从上往下合并也可。

#include <cstdio>
#include <vector>
using namespace std;

const int maxn=2e5+5;
int n, m, Q;
int fir[maxn], cnte;
struct Edge{
    int to, nxt;
}e[maxn*2];
void addedge(int x, int y){
    Edge &ed=e[++cnte];
    ed.to=y; ed.nxt=fir[x]; fir[x]=cnte; }

int euler[maxn*2], ftim[maxn], tim, st[maxn*2][20], dep[maxn];
void predfs(int u, int p){ int v;
    ftim[u]=tim; euler[tim++]=u; dep[u]=dep[p]+1;
    for (int i=fir[u]; i; i=e[i].nxt){
        if ((v=e[i].to)==p) continue;
        predfs(v, u); euler[tim++]=u; }
}

int lca(int x, int y){
    x=ftim[x]; y=ftim[y];
    if (x>y) swap(x, y); int c=-1;
    for (int l=y-x+1; l; l>>=1) ++c;  //这里求的是l是2的几次 
    if (dep[st[x][c]]<dep[st[y-(1<<c)+1][c]]) return st[x][c];
    else return st[y-(1<<c)+1][c];
}

int cntn, rt[maxn*2*20], lc[maxn*2*20], rc[maxn*2*20], seg[maxn*2*20];
void ins(int &x, int l, int r, int p){
    if (!x) x=++cntn; ++seg[x]; 
    if (l==r) return; int mid=(l+r)>>1; 
    if (mid>=p) ins(lc[x], l, mid, p);
    else ins(rc[x], mid+1, r, p);
}

int merge(int x, int y){  //把y合并到x上 
    //最底层的点是代表x和y最底层的和的 然后可以归纳法证明 
    if (!x) return y; if (!y) return x;
    seg[x]+=seg[y];
    lc[x]=merge(lc[x], lc[y]); 
    rc[x]=merge(rc[x], rc[y]);
    return x;
}

int query(int x, int l, int r, int k){
    if (l==r) return l; int mid=(l+r)>>1;
    if (seg[lc[x]]>=k) return query(lc[x], l, mid, k);
    else return query(rc[x], mid+1, r, k-seg[lc[x]]);
}

vector<int> q[maxn], id[maxn]; int ans[maxn];
void dfs(int u, int p){ int v;
    for (int j=fir[u]; j; j=e[j].nxt){
        if ((v=e[j].to)==p) continue;
        dfs(v, u); merge(rt[u], rt[v]);
    }
    for (int i=0; i<q[u].size(); ++i)
        ans[id[u][i]]=dep[u]-query(rt[u], 1, n, q[u][i]);
}

int main(){
    scanf("%d%d", &n, &m); int x, y, Lca;
    for (int i=1; i<n; ++i){
        scanf("%d%d", &x, &y);
        addedge(x, y); addedge(y, x); }
    dep[1]=1; predfs(1, 0); dep[0]=1e9;
    for (int i=0; i<tim; ++i) st[i][0]=euler[i];
    for (int i=1; i<20; ++i)
        for (int j=0; j<tim; ++j)
            if (dep[st[j][i-1]]<dep[st[j+(1<<i-1)][i-1]])
                st[j][i]=st[j][i-1]; 
            else st[j][i]=st[j+(1<<i-1)][i-1];
    for (int i=1; i<=n; ++i) rt[++cntn]=i;
    for (int i=1; i<=m; ++i){
        scanf("%d%d", &x, &y); Lca=lca(x, y);
        ins(rt[x], 1, n, dep[Lca]); ins(rt[y], 1, n, dep[Lca]);
    }
    scanf("%d", &Q);
    for (int i=1; i<=Q; ++i){
        scanf("%d%d", &x, &y); 
        q[x].push_back(y); id[x].push_back(i); }
    dfs(1, 0);
    for (int i=1; i<=Q; ++i) printf("%d\n", ans[i]<0?0:ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/MyNameIsPc/p/9635341.html
今日推荐