码迷,mamicode.com
首页 > 其他好文 > 详细

Count On A Tree 1

时间:2019-09-05 22:59:29      阅读:80      评论:0      收藏:0      [点我收藏+]

标签:参与   with   lse   理解   ret   问题   node   ast   false   

题意

给定一颗带点权有根树,每次查询两点之间路径上点权的第\(k\)大值


解法

求第\(k\)大?我们立刻想到主席树

但是这是树上的问题,我们怎么进行操作呢?

我们令每一颗主席树维护当前节点到根节点路径上的点权,那么每个点的主席树都由其父亲转移过来

那么怎么查询\(x,y\)两个结点路径上的第\(k\)大点权呢?

我们发现,想要获得\(x,y\)两个结点之间的路径对应的主席树,我们不能简单的提取\(x,y\)这两个点对应的主席树进行计算,因为我们会发现在之前的定义下,有一些多余的结点也算了进来:就是它们的\(lca\)到根节点路径上的点

那么我们在线段树上二分时现在需要传入三个点所对应的主席树\(x,y,LCA(x,y)\),每次减去\(LCA(x,y)\)上对应的结点

但是这样我们会发现\(LCA\)也是\(x,y\)路径上的结点,而它被多减了一次

所以我们还需要传入\(LCA\)的父亲参与线段树上二分

把整个过程可以理解为一个前缀和的操作(本身主席树就利用的是前缀和的思想)

最后成功的将\(x,y\)的路径提取出来


代码

#include <iostream>
#include <algorithm>

using namespace std;

void fast_IO();

const int N = 2e5 + 10;

int n, m, t, lstans;

int a[N], rt[N];

int w[N], dep[N], f[N][25];

int cap;
int head[N], to[N], nxt[N];

struct CTree {
    
    int sz;
    int ls[N * 20], rs[N * 20], val[N * 20];
    
    void clear() { sz = 0; }
    
    int newnode() {
        ++sz;
        ls[sz] = rs[sz] = val[sz] = 0;
        return sz;  
    }
    
    void mkchain(int &x, int y, int l, int r, int k) {
        x = newnode();
        ls[x] = ls[y], rs[x] = rs[y], val[x] = val[y] + 1;
        if (l == r) return;
        int mid = l + r >> 1;
        if (k <= mid)
            mkchain(ls[x], ls[y], l, mid, k);
        else
            mkchain(rs[x], rs[y], mid + 1, r, k);
    }
    
    int query(int x, int y, int z, int fz, int l, int r, int k) {
        if (l == r) return l;
        int tot = val[ls[x]] - val[ls[z]] + val[ls[y]] - val[ls[fz]];
        int mid = l + r >> 1;
        if (k <= tot)
            return query(ls[x], ls[y], ls[z], ls[fz], l, mid, k);
        else    
            return query(rs[x], rs[y], rs[z], rs[fz], mid + 1, r, k - tot);
    }   
    
} tr;

inline void add(int x, int y) {
    to[++cap] = y, nxt[cap] = head[x], head[x] = cap;
}

void discre() {
    for (int i = 1; i <= n; ++i)    a[i] = w[i];
    sort(a + 1, a + n + 1);
    t = unique(a + 1, a + n + 1) - a - 1;
    for (int i = 1; i <= n; ++i)
        w[i] = lower_bound(a + 1, a + t + 1, w[i]) - a;
}

void DFS(int x, int fa) {
    tr.mkchain(rt[x], rt[fa], 1, t, w[x]);
    dep[x] = dep[fa] + 1, f[x][0] = fa;
    for (int i = 1; i <= 20; ++i)   f[x][i] = f[f[x][i - 1]][i - 1];
    for (int i = head[x]; i; i = nxt[i]) 
        if (to[i] != fa)    DFS(to[i], x);
}

int LCA(int x, int y) {
    if (dep[x] < dep[y])    swap(x, y);
    for (int i = 20; i >= 0; --i)
        if (dep[f[x][i]] >= dep[y]) x = f[x][i];
    if (x == y) return y;
    for (int i = 20; i >= 0; --i)
        if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
    return f[x][0];
}

int main() {
    
    fast_IO();
    
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)    cin >> w[i];
    
    discre();
    
    int x, y;
    for (int i = 1; i < n; ++i) {
        cin >> x >> y;
        add(x, y), add(y, x);
    }
    
    tr.clear();
    DFS(1, 0);
    
    int k, lca, flca;
    for (int i = 1; i <= m; ++i) {
        cin >> x >> y >> k;
        x ^= lstans;
        lca = LCA(x, y), flca = f[lca][0];
        cout << (lstans = a[tr.query(rt[x], rt[y], rt[lca], rt[flca], 1, t, k)]) << endl;
    }
        
    return 0;
}

void fast_IO() {
    ios :: sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
}

Count On A Tree 1

标签:参与   with   lse   理解   ret   问题   node   ast   false   

原文地址:https://www.cnblogs.com/VeniVidiVici/p/11470262.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!