QTREE5 - Query on a tree V【动态点分治/点分树】

题目链接 SP2939 QTREE5 - Query on a tree V


  我们要求某点到最近白点的距离,支持多次修改多次询问。

  如果这题换成了最长距离,那么显然就没有这层关系了,我们就考虑这道题的动态点分治的做法。

查询答案

  如果现在的点的答案在目前点分树内,那么距离就一定是“该点到该点分树树根的距离”+“该点分树树根到最近白点的距离”。那么,有的人就会问了,如果在这个点分树到它的点分子树中间的路径上的话,那么距离不就是被扩大了嘛(比真实值要大的意思)。那好,我们来解释一下这个问题,如果说,在目前点分树中的答案算得到的距离是偏大的,那么也就是说它在上一个点分子树里面,那么也就是说,我们在上一棵点分树中已经算过了它的贡献了。所以说,不存在“偏大答案”被选取的情况,因为它在没“偏大”之前就已经被算掉了。

更新数据

  用一个小根堆来维护,也就是值小的在前,支持删除、插入操作,那么可以自己模拟一个堆(heap英文:堆)。然后不断的更新该点到目前点分树树根的距离,是删除还是插入。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define Big_INF 0x3f3f3f3f3f3f3f3f
#define eps 1e-6
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
inline int read()
{
    int x=0; char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x;
}
const int maxN = 1e5 + 7;
int N, Q, head[maxN], cnt, LOG_2[maxN << 1];
struct Eddge
{
    int nex, to;
    Eddge(int a=-1, int b=0):nex(a), to(b) {}
} edge[maxN << 1];
inline void addEddge(int u, int v)
{
    edge[cnt] = Eddge(head[u], v);
    head[u] = cnt++;
}
inline void _add(int u, int v) { addEddge(u, v); addEddge(v, u); }
struct Grand_Father
{
    int deep[maxN], euler[maxN << 1], Esiz, rid[maxN];
    void dfs(int u, int fa)
    {
        deep[u] = deep[fa] + 1; rid[u] = Esiz + 1;
        for(int i=head[u], v; ~i; i=edge[i].nex)
        {
            v = edge[i].to;
            if(v == fa) continue;
            euler[++Esiz] = u;
            dfs(v, u);
        }
        euler[++Esiz] = u;
    }
    int mn[maxN << 1][20];
    inline void RMQ_Init()
    {
        for(int i=1; i<=Esiz; i++) mn[i][0] = euler[i];
        for(int j=1; (1 << j) <= Esiz; j++)
        {
            for(int i=1; i + (1 << j) - 1 <= Esiz; i++)
            {
                if(deep[mn[i][j - 1]] < deep[mn[i + (1 << (j - 1))][j - 1]]) mn[i][j] = mn[i][j - 1];
                else mn[i][j] = mn[i + (1 << (j - 1))][j - 1];
            }
        }
    }
    inline int Rmq(int l, int r)
    {
        int det = r - l + 1, kk = LOG_2[det];
        if(deep[mn[l][kk]] <= deep[mn[r - (1 << kk) + 1][kk]]) return mn[l][kk];
        else return mn[r - (1 << kk) + 1][kk];
    }
    inline int _LCA(int u, int v)
    {
        int l = rid[u], r = rid[v];
        if(l > r) swap(l, r);
        return Rmq(l, r);
    }
    inline int _Dis(int u, int v)
    {
        int lca = _LCA(u, v);
        return deep[u] + deep[v] - 2 * deep[lca];
    }
} A_lca;
struct heap
{
    priority_queue<int, vector<int> , greater<int> > Que, Del;
    inline bool empty()
    {
        while(!Que.empty() && !Del.empty() && Que.top() == Del.top()) { Que.pop(); Del.pop(); }
        return Que.empty();
    }
    inline void push(int val) { Que.push(val); }
    inline void clear(int val) { Del.push(val); }
    inline int size() { return (int)(Que.size() - Del.size()); }
    inline int top()
    {
        while(!Que.empty() && !Del.empty() && Que.top() == Del.top()) { Que.pop(); Del.pop(); }
        return Que.top();
    }
    inline void pop()
    {
        while(!Que.empty() && !Del.empty() && Que.top() == Del.top()) { Que.pop(); Del.pop(); }
        Que.pop();
    }
} s[maxN];
int white_num = 0;
int siz[maxN], all, son[maxN], maxx, root, father[maxN];
bool vis[maxN] = {false};
void findroot(int u, int fa)
{
    siz[u] = 1; son[u] = 0;
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(vis[v] || v == fa) continue;
        findroot(v, u);
        siz[u] += siz[v];
        son[u] = max(son[u], siz[v]);
    }
    son[u] = max(son[u], all - siz[u]);
    if(son[u] < maxx) { maxx = son[u]; root = u; }
}
void Divide(int u)
{
    vis[u] = true;
    int totsiz = all;
    for(int i=head[u], v, rt; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(vis[v]) continue;
        all = siz[v] > siz[u] ? totsiz - siz[u] : siz[v];
        maxx = INF;
        findroot(v, 0);
        father[root] = u;
        rt = root;
        Divide(root);
    }
}
bool white[maxN] = {false};
inline void update(int u)
{
    int cop_u = u, dt;
    white[u] ^= 1;
    if(white[u])
    {
        white_num ++;
        while(u)
        {
            dt = A_lca._Dis(u, cop_u);
            s[u].push(dt);
            u = father[u];
        }
    }
    else
    {
        white_num --;
        while(u)
        {
            dt = A_lca._Dis(u, cop_u);
            s[u].clear(dt);
            u = father[u];
        }
    }
}
inline int query(int u)
{
    int cop_u = u, dt, ans = INF;
    while(u)
    {
        dt = A_lca._Dis(u, cop_u);
        if(!s[u].empty()) ans = min(ans, dt + s[u].top());
        u = father[u];
    }
    return ans;
}
inline void init()
{
    cnt = 0;
    for(int i=1; i<=N; i++) head[i] = -1;
    for(int i = 1, j = 2, k = 0; i<=(N << 1); i++)
    {
        if(i == j) { j <<= 1; k ++; }
        LOG_2[i] = k;
    }
}
int main()
{
    N = read();
    init();
    for(int i=1, u, v; i<N; i++)
    {
        u = read(); v = read();
        _add(u, v);
    }
    A_lca.dfs(1, 0);
    A_lca.RMQ_Init();
    all = N; maxx = INF;
    findroot(1, 0);
    Divide(root);
    Q = read();
    int x, op;
    while(Q--)
    {
        op = read(); x = read();
        if(op)
        {
            if(!white_num) printf("-1\n");
            else printf("%d\n", query(x));
        }
        else
        {
            update(x);
        }
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值