SPOJ - QTREE5 Query on a tree V 【动态点分治 + 堆维护】

传送门
题目大意: 两种树上操作, 树上的点原本都是黑色, 1: 反转一个点的颜色 2: 问询距离一个点最近的白色点(包括自己)

思路: 对于每一个点都维护一个堆, 表示的是距离这个点的左右白色点和到她的距离, 按距离维护即可. 然后每次询问一个点的时候就分别从当前点分别向它的重心跳然后询问答案即可, 树上距离就用LCA维护即可, 具体请看代码实现.

AC Code

const int maxn = 1e5 + 5;
int n, m, q;
// LCA
int up[maxn][23];
int dep[maxn], dis[maxn];
int cnt, head[maxn];
struct node {
    int to, next, w;
}e[maxn<<1];
void add(int u, int v, int w) {
    e[cnt] = node{v, head[u], w};
    head[u] = cnt++;
}
void dfs(int u,int fa,int d) {
    dep[u] = d + 1;
    for(int i = 1 ; i < 20 ; i ++) {
        up[u][i] = up[up[u][i-1]][i-1];
    }
    for(int i = head[u] ; ~i ; i = e[i].next) {
        int to = e[i].to;
        if(to == fa) continue;
        up[to][0] = u;
        dfs(to, u, d+1);
    }
}
int LCA_BZ(int u,int v) {
    int mx = 0;
    if(dep[u] < dep[v]) swap(u,v);
    int k = dep[u] - dep[v];
    for(int i = 19 ; i >= 0 ; i --) {
        if((1<<i) & k) {
            u = up[u][i];
        }
    }
    if(u == v) return u;
    for(int i = 19 ; i >= 0 ; i --) {
        if(up[u][i] != up[v][i]){
            u = up[u][i];
            v = up[v][i];
        }
    }
    return up[u][0];
}

// 点分治
int siz[maxn], mv[maxn], cur[maxn];
int root;
// 分别记录每个点的子树大小和, 最大的子树, 以及每个点的重心(递归的)
void getroot(int u, int fa) {
    siz[u] = 1;
    for (int i = head[u]; ~i; i = e[i].next) {
        int to = e[i].to;
        if (to == fa || cur[to] != -1) continue;
        getroot(to, u);
        siz[u] += siz[to];
    }
}

void getAllRoot(int u, int fa, int tot, int pret) {
    for (int i = head[u]; ~i; i = e[i].next) {
        int to = e[i].to;
        if (to == fa || cur[to] != -1 || 2*siz[to] <= tot) continue;
        getAllRoot(to, u, tot, pret); return ;
    }
    cur[u] = pret;
    for (int i = head[u] ; ~i ; i = e[i].next) {
        int to = e[i].to;
        if (cur[to] != -1) continue;
        getroot(to, -1);  // 每次得到一个新的根后要重新
        //计算这些点到根的这些距离, 因为不在是之前的那个距离了.
        getAllRoot(to, u, siz[to], u);
    }
}

// 需要维护的堆
int white[maxn]; // 记录颜色
struct path {
    int to, d;
    bool operator < (const path &_) const {
        return d > _.d;
    }
};
priority_queue<path>que[maxn];
int dis_white(int x) {  // 算距离x最近的白色的点
    while(!que[x].empty()) {
        path u = que[x].top();
        if (!white[u.to]) que[x].pop();
        else return u.d;
    }
    return inf;
}
void init() {
    cnt = 0; Fill(head, -1);
    Fill(cur, -1);
}
void solve() {
    scanf("%d", &n);
    init();
    for (int i = 1 ; i < n ; i ++) {
        int u, v;
        scanf("%d%d", &u, &v);
        add(u, v, 1); add(v, u, 1);
    }
    dfs(1, -1, 0);
    mv[root=0] = n; getroot(1, -1);
    getAllRoot(1, -1, n, -2);
    scanf("%d", &q); int cnt = 0;
    while(q--) {
        int op, x;
                scanf("%d%d", &op, &x);
        if (!op) {
            white[x] = 1 - white[x];
            if (white[x]) ++ cnt;
            else -- cnt;
            if (white[x]) {
                int r = x;
                while(r != -2) {
                    que[r].push(path{x, dep[r] + dep[x] - 2*dep[LCA_BZ(r, x)]});
                    r = cur[r];
                }
            }
        }
        else {
            if (!cnt) puts("-1");
            else if (white[x]) puts("0");
            else{
            int ans = inf;
            int r = x;
            while(r != -2) {
                ans = min(ans, dep[r] + dep[x] - 2*dep[LCA_BZ(r, x)] + dis_white(r));
                r = cur[r];
            }
            printf("%d\n", ans);
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Anxdada/article/details/81279849
今日推荐