SPOJ 10628. SPOJ COT Count on a tree

  这题是裸的主席树,每个节点建一棵主席树,再加个lca就可以了。

  历尽艰辛,终于A掉了这一题,这般艰辛也显示出了打代码的不熟练。

  错误:1、lca倍增的时候i和j写反了,RE了5次,实在要吸取教训

     2、主席树插入操作的时候,如果插入到的那个点(叶节点)原来有值,而没有加上,导致了WA

  以下是历尽艰辛的代码,还很长。

 

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <map>

using namespace std;

const int maxn = 100005;
int n, m, w[maxn];
int head[maxn], label;
struct Edge
{
    int v, next;
    Edge (int v = 0, int next = 0):
        v(v), next(next) {}
}e[maxn*2];
int depth[maxn], f[maxn][31], root[maxn], temp[maxn], s_num;
queue <int> q;
map <int, int> id;
struct Tree
{
    int sum[maxn*20], ls[maxn*20], rs[maxn*20], cnt;
    Tree ()
    {
        sum[0] = 0, cnt = 0;
    }
    void PushUp(int rt)
    {
        sum[rt] = sum[ls[rt]]+sum[rs[rt]];
    }
    void insert(int las_rt, int rt, int l, int r, int p, int d)
    {
        if (l == r)
        {
            sum[rt] = sum[las_rt]+d;
            return ;
        }
        int mid = (l+r)>>1;
        if (p <= mid)
        {
            ls[rt] = ++cnt, rs[rt] = rs[las_rt];
            insert(ls[las_rt], ls[rt], l, mid, p, d);
        }
        else
        {
            ls[rt] = ls[las_rt], rs[rt] = ++cnt;
            insert(rs[las_rt], rs[rt], mid+1, r, p, d);
        }
        PushUp(rt);
    }
    int query(int u_rt, int v_rt, int lca_rt, int lca_pos, int l, int r, int k)
    {
        if (l == r)
            return l;
        int mid = (l+r)>>1;
        int s_l = sum[ls[u_rt]]+sum[ls[v_rt]]-2*sum[ls[lca_rt]]+(lca_pos >= l && lca_pos <= mid);
        if (k <= s_l)
            return query(ls[u_rt], ls[v_rt], ls[lca_rt], lca_pos, l, mid, k);
        else
            return query(rs[u_rt], rs[v_rt], rs[lca_rt], lca_pos, mid+1, r, k-s_l);
    }
}T;

void ins(int u, int v)
{
    e[++label] = Edge(v, head[u]);
    head[u] = label;
}

void in()
{
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &w[i]);
    for (int i = 1; i <= n; ++i)
        head[i] = -1;
    label = -1;
    for (int i = 1; i < n; ++i)
    {
        int u, v;
        scanf("%d %d", &u, &v);
        ins(u, v), ins(v, u);
    }
}

void Build_lca()
{
    for (int i = 0; i <= 30; ++i)
        for (int j = 1; j <= n; ++j)
            f[j][i] = -1;
    q.push(1);
    depth[1] = 0;
    f[1][0] = -1;
    while (!q.empty())
    {
        int u = q.front();
        for (int i = head[u]; i != -1; i = e[i].next)
        {
            int v = e[i].v;
            if (v == f[u][0])
                continue ;
            f[v][0] = u;
            depth[v] = depth[u]+1;
            q.push(v);
        }
        q.pop();
    }
    for (int i = 1; i <= 30; ++i)
        for (int j = 1; j <= n; ++j)
        {
            if (f[j][i-1] == -1)
                continue ;
            f[j][i] = f[f[j][i-1]][i-1];
        }
}

void Hash_a()
{
    for (int i = 1; i <= n; ++i)
        temp[i] = w[i];
    sort(temp+1, temp+n+1);
    s_num = 0;
    for (int i = 1; i <= n; ++i)
        if (temp[i] != temp[i-1] || i == 1)
        {
            temp[++s_num] = temp[i];
            id[temp[i]] = s_num;
        }
}

void dfs(int u)
{
    root[u] = ++T.cnt;
    T.insert(u == 1 ? 0 : root[f[u][0]], root[u], 1, s_num, id[w[u]], 1);
    for (int i = head[u]; i != -1; i = e[i].next)
    {
        int v = e[i].v;
        if (v == f[u][0])
            continue ;
        dfs(v);
    }
}

void Build_tree()
{
    Hash_a();
    dfs(1);/*
    for (int i = 1; i <= n; ++i)
        printf("%d %d\n", i, T.sum[root[i]]);*/
}

void prepare()
{
    Build_lca();
    Build_tree();
}

int lca(int u, int v)
{
    if (depth[u] < depth[v])
        swap(u, v);
    for (int i = 30; i >= 0; --i)
    {
        if (f[u][i] == -1)
            continue ;
        if (depth[f[u][i]] >= depth[v])
        {
            u = f[u][i];
            if (depth[u] == depth[v])
                break ;
        }
    }
    if (u == v)
        return u;
    for (int i = 30; i >= 0; --i)
    {
        if (f[u][i] == -1)
            continue ;
        if (f[u][i] != f[v][i])
        {
            u = f[u][i];
            v = f[v][i];
        }
    }
    return f[u][0];
}

void work()
{
    prepare();
    while (m --)
    {
        int u, v, k;
        scanf("%d %d %d", &u, &v, &k);
        int t = lca(u, v);
        int pos = T.query(root[u], root[v], root[t], id[w[t]], 1, s_num, k);
        printf("%d\n", temp[pos]);
    }
}

int main()
{
    in();
    work();
    return 0;
}

  

时间: 2024-08-09 20:30:44

SPOJ 10628. SPOJ COT Count on a tree的相关文章

spoj cot: Count on a tree 主席树

10628. Count on a tree Problem code: COT You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight. We will ask you to perform the following operation: u v k : ask for the kth minimum weight on the path

spoj COT - Count on a tree (树上第K小 LCA+主席树)

链接: https://www.spoj.com/problems/COT/en/ 思路: 首先看到求两点之前的第k小很容易想到用主席树去写,但是主席树处理的是线性结构,而这道题要求的是树形结构,我们可以用dfs跑出所有点离根的距离-dep[i](根为1,dep[1]也为1)在dfs的过程 中,我们对每一个节点建一棵线段树,那么[a,b]就是:root[a] + root[b] - root[lca(a,b)] - root[f[lca(a,b)]]; (因为a-b的路径上的权值还要算上lca(

SPOJ COT Count on a tree(主席树+倍增lca)

思路:这个题其实就是树上的第k小,主席树的本质还是类似于前缀和一样的结构,所以是完全相同的,所以我们在树上也可以用同样的方法,我们对于每一个节点进行建树,然后和普通的树上相同,ab之间的距离是等于 root[a]+root[b]-root[lca[a,b]]-root[fa[lca[a,b]]] 代码: 复制代码#include <bits/stdc++.h>using namespace std;const int maxn=1e5+7;const int POW=18;int num[ma

spoj COT - Count on a tree(主席树 +lca,树上第K大)

您将获得一个包含N个节点的树.树节点的编号从1到?.每个节点都有一个整数权重. 我们会要求您执行以下操作: uvk:询问从节点u到节点v的路径上的第k个最小权重 输入 在第一行中有两个整数?和中号.(N,M <= 100000) 在第二行中有N个整数.第i个整数表示第i个节点的权重. 在接下来的N-1行中,每行包含两个整数u v,它描述了一个边(u,v). 在接下来的M行中,每行包含三个整数u v k,这意味着要求从节点u到节点v的路径上的第k个最小权重的操作. 解题思路: 首先对于求第K小的问

SPOJ COT Count on a tree(树上主席树 + LCA 求路径第k小)题解

题意:n个点的树,每个点有权值,问你u~v路径第k小的点的权值是? 思路: 树上主席树就是每个点建一棵权值线段树,具体看JQ博客,LCA用倍增logn求出,具体原理看这里 树上主席树我每个点的存的是点u到源点1的权值线段树,那我求点u到v的所有点,显然是 u + v - lca - fa[lca],就是u到1 + v到1 - 多算的lca - 多算的fa[lca].不能减去两个lca不然少一个点了, LCA板子: //LCA int fa[maxn][20]; int dep[maxn]; vo

SP10628 COT - Count on a tree

主席树的综合运用题. 前置芝士 可持久化线段树:其实就是主席树了. LCA:最近公共祖先,本题需要在\(\log_2N\)及以内的时间复杂度内解决这个问题. 具体做法 主席树维护每个点到根节点这一条链上不同树出现的次数,然后发现这个东西是可以相减的,于是这条链上每个数出现的次数就变成了\(sum[u]+sum[v]-2*sum[LCA(u,v)]\).然后就可以发现这个是错的,如果按这个式子计算最后的链上就没有LCA位置的值了,所以在范围包含\(val[LCA(u,v)]\)时需要加一,然后正常

SPOJ 10628 Count on a tree(Tarjan离线LCA+主席树求树上第K小)

COT - Count on a tree #tree You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight. We will ask you to perform the following operation: u v k : ask for the kth minimum weight on the path from node u 

BZOJ 2588: Spoj 10628. Count on a tree 主席树+lca

2588: Spoj 10628. Count on a tree Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v,k),表示一组询问.

bzoj 2588: Spoj 10628. Count on a tree LCA+主席树

2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数