bzoj 2588 Count on a tree 解题报告

Count on a tree

题目描述

给定一棵\(N\)个节点的树,每个点有一个权值,对于\(M\)个询问\((u,v,k)\),你需要回答\(u\) \(xor\) \(lastans\)和\(v\)这两个节点间第\(K\)小的点权。其中\(lastans\)是上一个询问的答案,初始为\(0\),即第一个询问的u是明文。

输入输出格式

输入格式:

第一行两个整数\(N,M\)。

第二行有\(N\)个整数,其中第\(i\)个整数表示点\(i\)的权值。

后面\(N-1\)行每行两个整数\((x,y)\),表示点\(x\)到点\(y\)有一条边。

最后\(M\)行每行两个整数\((u,v,k)\),表示一组询问。

输出格式:

\(M\)行,表示每个询问的答案。



一看是无修改的第\(k\)值查询,我们可以用可持久化降维。

就是把序列上的第\(k\)值扩展到了树上。

我们考虑一条树上路径可以被怎么表示

这样类比,假设树上每个点有点权,则树上路径点权之和可以被树的前缀和数组这样表示

\(len(u,v)=dis[u]+dis[v]-dis[lca(u,v)]-dis[father(lca[u,v])]\)

然而前缀和其实就是一维的可持久化,我们把\(dis\)数组类比成主席树加加减减就好了



Code:

#include <cstdio>
#include <algorithm>
#define ls ch[now][0]
#define rs ch[now][1]
const int N=100010;
int Next[N<<1],to[N<<1],head[N],cnt;
void add(int u,int v)
{
    Next[++cnt]=head[u];to[cnt]=v;head[u]=cnt;
}
int sum[N*25],ch[N*25][2],tot,n,m,n_;
void updata(int now)
{
    sum[now]=sum[ls]+sum[rs];
}
int rebuild(int las,int l,int r,int pos)
{
    int now=++tot;
    if(l==r)
    {
        sum[now]=sum[las]+1;
        return now;
    }
    int mid=l+r>>1;
    if(pos<=mid)
    {
        ls=rebuild(ch[las][0],l,mid,pos);
        rs=ch[las][1];
    }
    else
    {
        ls=ch[las][0];
        rs=rebuild(ch[las][1],mid+1,r,pos);
    }
    updata(now);
    return now;
}
int ha[N],loc[N],root[N];
int query(int u,int v,int lca,int lcaf,int l,int r,int k)
{
    if(l==r) return ha[l];
    int s=sum[ch[u][0]]+sum[ch[v][0]]-sum[ch[lca][0]]-sum[ch[lcaf][0]];
    int mid=l+r>>1;
    if(k<=s) return query(ch[u][0],ch[v][0],ch[lca][0],ch[lcaf][0],l,mid,k);
    else return query(ch[u][1],ch[v][1],ch[lca][1],ch[lcaf][1],mid+1,r,k-s);
}
int top[N],dfn[N],f[N],dep[N],ws[N],siz[N],time;
void dfs1(int now)
{
    root[now]=rebuild(root[f[now]],1,n,loc[now]);
    siz[now]++;
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(v!=f[now])
        {
            f[v]=now;
            dep[v]=dep[now]+1;
            dfs1(v);
            siz[now]+=siz[v];
            if(siz[ws[now]]<siz[v])
                ws[now]=v;
        }
    }
}
void dfs2(int now,int anc)
{
    dfn[now]=++time;
    top[now]=anc;
    if(ws[now]) dfs2(ws[now],anc);
    for(int i=head[now];i;i=Next[i])
        if(!dfn[to[i]])
            dfs2(to[i],to[i]);
}
int LCA(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])
            x=f[top[x]];
        else
            y=f[top[y]];
    }
    return dep[x]<dep[y]?x:y;
}
std::pair <int,int > node[N];
void init()
{
    scanf("%d%d",&n_,&m);
    for(int d,i=1;i<=n_;i++)
    {
        scanf("%d",&d);
        node[i]=std::make_pair(d,i);
    }
    std::sort(node+1,node+1+n_);
    for(int i=1;i<=n_;i++)
    {
        if(node[i].first!=node[i-1].first) n++;
        ha[n]=node[i].first;
        loc[node[i].second]=n;
    }
    for(int u,v,i=1;i<n_;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs1(1);
    dfs2(1,1);
}
void work()
{
    for(int u,v,lca,k,lastans=0,i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&k);
        u^=lastans;
        lca=LCA(u,v);
        printf("%d\n",lastans=query(root[u],root[v],root[lca],root[f[lca]],1,n,k));
    }
}
int main()
{
    init();
    work();
    return 0;
}


2018.7.31

原文地址:https://www.cnblogs.com/ppprseter/p/9396128.html

时间: 2024-12-07 14:46:47

bzoj 2588 Count on a tree 解题报告的相关文章

BZOJ 2588 Count on a tree 主席树+倍增LCA

题目大意:给定一棵树,每个节点有权值,询问两个节点路径上的权值第k小 这题很卡时间... 树链剖分+二分+树套树的O(nlog^4n)做法可以去死了 没有修改操作,树链剖分+二分+划分树O(nlog^3n),还是死了 我怒了,裸学了一发可持久化线段树(不看任何代码OTZ,我是怎么做到的0.0),二分+主席树,O(nlog^2n),居然还是死了! 最后发现我SB了,完全没有必要二分,直接把4个参全传下去就行了,O(nlogn) 首先我们对于每个节点维护这个节点到根的权值线段树 然后对于每个询问(x

BZOJ 2588 Count on a tree (COT) 是持久的段树

标题效果:两棵树之间的首次查询k大点的权利. 思维:树木覆盖树,事实上,它是正常的树木覆盖了持久段树. 由于使用权值段树可以寻求区间k大,然后应用到持久段树思想,间隔可以做减法.详见代码. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define NIL (tree[0]) using names

BZOJ 2588 Count on a tree (COT) 可持久化线段树

题目大意:查询树上两点之间的第k大的点权. 思路:树套树,其实是正常的树套一个可持久化线段树.因为利用权值线段树可以求区间第k大,然后再应用可持久化线段树的思想,可以做到区间减法.详见代码. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define NIL (tree[0]) using name

bzoj 2588 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),表示一组询问. Output M行,表示每个询问的答案.最后一个询问不输出换行符 S

【LeetCode】Symmetric Tree 解题报告

Symmetric Tree 解题报告 [LeetCode] https://leetcode.com/problems/symmetric-tree/ Total Accepted: 106639 Total Submissions: 313969 Difficulty: Easy Question Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For

【原创】leetCodeOj ---Convert Sorted List to Binary Search Tree 解题报告

原题地址: https://oj.leetcode.com/problems/convert-sorted-list-to-binary-search-tree/ 题目内容: Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. 方法: 单纯.如何安排插入顺序,使得一棵二叉排序树接近平衡? 你从中间开始插嘛.这样左子树和右子树之差或

655. Print Binary Tree 解题报告(树)

第一部分:搜索.遍历 [例子1]655. Print Binary Tree Example 1: Input: 1 / 2 Output: [["", "1", ""], ["2", "", ""]] Example 2: Input: 1 / 2 3 4 Output: [["", "", "", "1"

【LeetCode】Convert Sorted List to Binary Search Tree 解题报告

[题目] Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. [解析] 分治:用快慢指针找到链表的中点,作为树的root,然后二分--中点之前的链表和中点之后的链表分别再构造二叉平衡树. /** * Definition for singly-linked list. * public class ListNode { * int

LeetCode Convert Sorted List to Binary Search Tree 解题报告

Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. 从给定的有序链表生成平衡二叉树. 解题思路:最容易想到的就是利用数组生成二叉树的方法,找到中间节点作为二叉树的root节点,然后分别对左右链表递归调用分别生成左子树和右子树.时间复杂度O(N*lgN) AC代码: public class Solution { ListNode