某主席树的的问题

13 篇文章 0 订阅
11 篇文章 0 订阅

Description

给定一棵 N 个节点的树, 每个点 i 有权值 a[i] , 1 <= a[i] <= M . 有 Q 个询问, 对于询问 x,y,k , 分别输出树上从 x 到 y 的路径中, 权值小于/等于/大于 k 的点的数目.

Solution

恩。。权值线段树?还要判断区间,好吧上主席树咯。如果不在树上,那么查询[l,r]关于小于k的数目。我们对区间建立线段树,可持久化权值(不知道能不能这么表述)。反正就是建立[1,n]的线段树,从小到大插入权值,比如tree[m]表示已经把小于等于m的权值的点插入到树中了,而主席树是可减的,那么问题就解决了。答案就是
query(Tree: tree[k]-tree[0], Range: [l, r])
拿到树上了呢?可能最容易想到的就是树链剖分了,简单实现一下,可能会爆栈,要用bfs。时间复杂度是 O(Qlog2N) ,是否有优化空间?有,考虑到我们查询树链只想知道满足小于k的数目而不关心到底怎么个小于,即顺序,而且树链是可加的。所以我们用LCA解决即可。对每个点建立权值线段树,那么查询树链[x,y]就变成了:[root,x]+[root,y]-2*[root,lca(x,y)]。时间复杂度变为 O(QlogN)

只有一个树链剖分的。。。。。

#include <cstdio>
#include <algorithm>
#include <ctime>
using namespace std;
const int N = 262190, M = 131080;

int read() {
    int s = 0, f = 1; char ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
    for (; '0' <= ch && ch <= '9'; ch = getchar()) s = s * 10 + ch - '0';
    return s * f;
}

struct Seg {
    Seg *lc, *rc;
    int s;

    Seg() { lc = rc = this; s = 0; }
    Seg(Seg *_l, Seg *_r, int _s) : lc(_l), rc(_r), s(_s) {}

    void *operator new(size_t) {
        static Seg pool[M * 60], *C = pool;
        return C++;
    }

    Seg *put(int l, int r, int pos) {
        int mid = l + r >> 1;
        if (l == r) return new Seg(lc, rc, s + 1);
        else if (pos <= mid) return new Seg(lc->put(l, mid, pos), rc, s + 1);
        else return new Seg(lc, rc->put(mid + 1, r, pos), s + 1);
    }

    static int get(Seg *s1, Seg *s2, int l, int r, int ql, int qr) {
        int mid = l + r >> 1;
        if (ql == l && qr == r) return s1->s - s2->s;
        if (mid < ql) return get(s1->rc, s2->rc, mid + 1, r, ql, qr);
        else if (qr <= mid) return get(s1->lc, s2->lc, l, mid, ql, qr);
        else return get(s1->rc, s2->rc, mid + 1, r, mid + 1, qr) + get(s1->lc, s2->lc, l, mid, ql, mid);
    }
} *tree[M];

pair<int, int> a[N];

int h[N], p[N * 2], v[N * 2], cnt = 0, id = 0, n;
int pos[N], top[N], son[N], fa[N], dep[N], sz[N];
void add(int x, int y) {
    p[++cnt] = h[x]; v[cnt] = y; h[x] = cnt;
    p[++cnt] = h[y]; v[cnt] = x; h[y] = cnt;
}

void dfs(int x) {
    son[x] = 0; sz[x] = 1;
    for (int i = h[x]; i; i = p[i])
        if (v[i] != fa[x]) {
            fa[v[i]] = x; dep[v[i]] = dep[x] + 1;
            dfs(v[i]); sz[x] += sz[v[i]];
            if (sz[son[x]] < sz[v[i]]) son[x] = v[i];
        }
}

void dfs2(int x, int t) {
    top[x] = t; pos[x] = ++id;
    if (son[x]) dfs2(son[x], t);
    for (int i = h[x]; i; i = p[i])
        if (v[i] != son[x] && v[i] != fa[x])
            dfs2(v[i], v[i]);
}
/*
int q[N];

void bfs() {
    int x, f = 0, r = 0;
    q[r++] = 1;
    while (f < r) {
        x = q[f++];
        dep[x] = dep[fa[x]] + 1;
        sz[x] = 1;
        for(int i = h[x]; i; i = p[i])
            if (v[i] != fa[x])
                fa[v[i]] = x, q[r++] = v[i];
    }
    for (; f >= 2; f--) {
        x = q[f - 1];
        sz[fa[x]] += sz[x];
        if(sz[x] > sz[son[fa[x]]]) son[fa[x]] = x;
    }
    for(f = 1; f <= r; f++) {
        x = q[f];
        if (son[fa[x]] != x) top[x] = x;
        else top[x] = top[fa[x]];
    }  
}
*/
int query(int x, int y, int upper, int lower) {
    int fx = top[x], fy = top[y], ans = 0;
    while (fx != fy) {
        if (dep[fx] < dep[fy]) swap(fx, fy), swap(x, y);
        ans += Seg::get(tree[upper], tree[lower], 1, n, pos[fx], pos[x]);
        x = fa[fx], fx = top[x];
    }
    if (dep[x] > dep[y]) swap(x, y);
    return ans + Seg::get(tree[upper], tree[lower], 1, n, pos[x], pos[y]);
}

int main() {
    int m, q, i, x, y, k, q1, q2, q3;
    n = read(), m = read(), q = read();
    int lastans = 0;
    for (i = 1; i <= n; i++) a[i].first = read();
    for (i = 1; i < n; i++) add(read(), read());
    dfs(1); dfs2(1, 0);
    //bfs();
    for (i = 1; i <= n; i++) a[i].second = pos[i];
    sort(a + 1, a + n + 1);
    tree[0] = new Seg();
    for (i = 1; i <= n; i++) tree[a[i].first] = tree[a[i - 1].first]->put(1, n, a[i].second);
    for (i = 0; i <= m; i++) if (tree[i] == NULL) tree[i] = tree[i - 1];
    while (q--) {
        x = read() ^ lastans, y = read() ^ lastans, k = read() ^ lastans;
        q1 = query(x, y, k - 1, 0); q2 = query(x, y, k, k - 1); q3 = query(x, y, m, k);
        printf("%d %d %d\n", q1, q2, q3);
        lastans = q1 ^ q2 ^ q3;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值