P2633|主席树+dfs序+树链剖分求lca+离散化

不知道为什么会RE。。
待补

思路:链上求u和v两点路径第k小利用lca就转变为了 U+V-LCA-FA(LCA) 上的第k小,这因为每个点的主席树的root是从其父转移来的。可以用树链剖分求lca;在dfs序上建立主席树将树上问题转变为区间问题,询问的时候用主席树求区间k小值。

终于能写出这种题了,开心!

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5+100;
int n,m,e = 1,num,ans=0;
int mp[maxn],b[maxn],root[maxn];
vector<int> g[maxn];
/*  父亲,    深度,       子节点数, 重儿子,   dfs序,   dfs映射,链头,    链尾    */
int fa[maxn],depth[maxn],sz[maxn],son[maxn],id[maxn],rk[maxn],top[maxn],bot[maxn];
int cnt = 0;

struct Node{int v,lc,rc;}T[maxn*24];
struct A{
    int x,idx;
    bool operator < (const A &temp)const{
        return x < temp.x;
    }
}a[maxn];
int ranks[maxn];

//树链剖分求lca部分
void dfs1(int x,int deep){
    depth[x] = deep;
    sz[x] = 1;
    for(int li = 0;li<g[x].size();li++){
        int i = g[x][li];
        if(i == fa[x]) continue;
        fa[i] = x;
        dfs1(i,deep+1);
        sz[x] += sz[i];
        if(sz[i] > sz[son[x]]) son[x] = i;
    }
}

void dfs2(int x,int tp){
    top[x] = tp;
    id[x] = ++cnt;
    rk[cnt] = x;
    if(son[x]) dfs2(son[x],tp),bot[x] = bot[son[x]];
    else bot[x] = x;
    for(int li=0;li<g[x].size();li++){
        int i = g[x][li];
        if(i != fa[x] && i != son[x])
            dfs2(i,i);
    }
}

int lca(int u,int v){
    while(top[u] != top[v]){
        if(depth[top[u]] < depth[top[v]]) swap(u,v);
        u = fa[top[u]];
    }
    if(depth[u] < depth[v]) return u;
    return v;
}

//主席树插入insert
void insert(int pre,int cur,int pos,int l,int r){
    if(l == r){
        T[cur].v = T[pre].v + 1;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid){
        T[cur].lc = ++e;
        T[cur].rc = T[pre].rc;
        insert(T[pre].lc,T[cur].lc,pos,l,mid);
    }else{
        T[cur].rc = ++e;
        T[cur].lc = T[pre].lc;
        insert(T[pre].rc,T[cur].rc,pos,mid+1,r);
    }
    T[cur].v = T[T[cur].lc].v + T[T[cur].rc].v;
}

//主席树求第k小
int kth(int ql,int qr,int LCA,int FLCA,int k,int l,int r){
    if(l == r) return l;
    int mid = (l + r) >> 1;
    int sum = T[T[ql].lc].v + T[T[qr].lc].v + T[T[LCA].lc].v - T[T[FLCA].lc].v;
    if(sum >= k) return kth(T[ql].lc,T[qr].lc,T[LCA].lc,T[FLCA].lc,k,l,mid);
    else return kth(T[ql].rc,T[qr].rc,T[LCA].rc,T[FLCA].rc,k-sum,mid+1,r);
}

//dfs序上建树
void dfs(int u){
    root[u] = ++e;
    insert(root[fa[u]],root[u],b[u],1,num);
    for(int i=0;i<g[u].size();i++){
        int v = g[u][i];
        if(v!=fa[u]) dfs(v);
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i].x;
        a[i].idx = i;
    }
    //离散化
    sort(a+1,a+n+1);
    b[a[1].x] = ++num;
    mp[num] = a[1].x;
    for(int i=2;i<=n;i++){
        if(a[i].x != a[i-1].x) ++num;
        b[a[i].idx] = num; //b数组保存新的编号
        mp[num] = a[i].x; //mp数组存储原始权值
    }
    int u,v,k;
    for(int i=1;i<n;i++){
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs1(1,1);
    dfs2(1,1);
    dfs(1);
    while(m--){
        cin>>u>>v>>k;
        u ^= ans;
        int LCA = lca(u,v);
        ans = mp[kth(root[u],root[v],root[LCA],root[fa[LCA]],k,1,num)];
        cout<<ans<<endl;
    }
    return 0;
} 

原文地址:https://www.cnblogs.com/fisherss/p/12230425.html

时间: 2024-08-06 22:21:08

P2633|主席树+dfs序+树链剖分求lca+离散化的相关文章

树链剖分求LCA

这里先推荐两道练习的裸题 首先是求点 [codevs4605] LCA 就是求两个点的公共祖先,每次询问xor上上一个询问的答案. 先是两遍DFS: dfs1:把dep.siz.son求出来 dfs2:求出top和w siz[v]表示以v为根的子树的节点数 dep[v]表示v的深度(根深度为1) top[v]表示v所在的链的顶端节点 fa[v]表示v的父亲 son[v]表示与v在同一重链上的v的儿子节点 w[v]结点编号 int lca(int x,int y){ while (top[x]!=

【Codeforces163E】e-Government AC自动机fail树 + DFS序 + 树状数组

E. e-Government time limit per test:1 second memory limit per test:256 megabytes input:standard input output:standard output The best programmers of Embezzland compete to develop a part of the project called "e-Government" — the system of automa

【BZOJ-2434】阿狸的打字机 AC自动机 + Fail树 + DFS序 + 树状数组

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2022  Solved: 1158[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的:l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最

【POJ1330】Nearest Common Ancestors(树链剖分求LCA)

Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below: In the figure, each node is labeled with an integer from {1, 2,...,16}. Node 8 is the root of the tree. Node x is an ancestor of

【模板】树链剖分求LCA

洛谷3379 1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int maxn=500010,inf=1e9; 5 int n,m,x,y,root,tot,dep[maxn],son[maxn],size[maxn],fa[maxn],top[maxn],last[maxn]; 6 struct edge{int to,pre;}e[maxn<<1]; 7 inline v

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

51 nod 1681 公共祖先 (主席树+dfs序)

1681 公共祖先 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 有一个庞大的家族,共n人.已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边). 在另一个未知的平行宇宙,这n人的祖辈关系仍然是树形结构,但他们相互之间的关系却完全不同了,原来的祖先可能变成了后代,后代变成的同辈…… 两个人的亲密度定义为在这两个平行宇宙有多少人一直是他们的公共祖先. 整个家族的亲密度定义为任意两个人亲密度的总和. Input 第一行一个数n(1<=n<=100000)

【BZOJ1803】Spoj1487 Query on a tree III 主席树+DFS序

[BZOJ1803]Spoj1487 Query on a tree III Description You are given a node-labeled rooted tree with n nodes. Define the query (x, k): Find the node whose label is k-th largest in the subtree of the node x. Assume no two nodes have the same labels. Input