关闭

[bzoj3123][洛谷P3302] [SDOI2013]森林(树上主席树+倍增lca+启发式合并)

236人阅读 评论(0) 收藏 举报
分类:

传送门(luogu)
传送门(bzoj)
此题有两种操作:
1.查询树上两点间权值第k小
2.连接两棵树
限制条件:强制在线
看到第k小大家想到的肯定是主席树,可是连边又让大家想到了LCT
我们选择使用主席树。
为什么呢?
我们肯定是要舍弃两种操作中的一种,让它变慢,另一个就快了。
然而,第k小显然没有什么优化的余地,可是连接两棵树显然就是合并两棵树
合并!我们可以想到启发式合并!它可以优化合并到O(logn),一个log的消耗还是可以承受的。
于是具体实现就是,我们可以记录每棵树的大小,合并的时候把小的接到大的上面去,合并的时候我们dfs暴力修改,用父节点重建每个节点的主席树,并且更新每个节点的倍增数组(st表)。
更新倍增数组这里还是有技巧的,我们不需要dfs完了才修改,我们可以一边dfs一边改:

void dfs(int x,int father,int rt){
    //这一段
    st[x][0]=father;
    for(int k=1;k<=16;k++){
        st[x][k]=st[st[x][k-1]][k-1];
    }
    //上面
    son[rt]++;
    dep[x]=dep[father]+1;
    fa[x]=father;
    vis[x]=1;
    insert(root[x],root[father],1,size,Hash(a[x]));
    for(int i=head[x];i;i=e[i].next){
        int u=e[i].to;
        if(u==father)continue;
        dfs(u,x,rt);
    }
}

这就是如何合并。
然后我们来讨论查询。
主席树大家肯定都写过,但是大部分人写的都是针对数组的,这里我们要在树上建主席树。
每一颗主席树维护的是它到根节点的数字信息,于是类似于原本的主席树,我们可以发现每一个节点和它的父亲节点维护的主席树没有太大的变化,最多只会变化logn个点,于是我们dfs时可以利用每个节点的父亲的主席树来建立它的主席树。
查询时怎么做呢?
我们可以记录四个变量,从四个节点同时查询
假设查询节点分别是x,y,那它们分别就是:x,y,lca(x,y),father(lca(x,y))
每次主席树操作时,就是这样:

int query(int x,int y,int pre1,int pre2,int l,int r,int k){
    if(l==r)return b[l];
    int lsize=t[t[x].ls].size+t[t[y].ls].size-t[t[pre1].ls].size-t[t[pre2].ls].size;
    int mid=(l+r)>>1;
    if(k<=lsize)return query(t[x].ls,t[y].ls,t[pre1].ls,t[pre2].ls,l,mid,k);
    else return query(t[x].rs,t[y].rs,t[pre1].rs,t[pre2].rs,mid+1,r,k-lsize);
}

建树和原来一样:

void insert(int &now,int pre,int l,int r,int x){
    now=++cnt;
    t[now]=t[pre];
    t[now].size++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)insert(t[now].ls,t[pre].ls,l,mid,x);
    else insert(t[now].rs,t[pre].rs,mid+1,r,x);
}

每个根节点还要先build一下:

void build(int &now,int l,int r){
    now=++cnt;
    t[now].size=0;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(t[now].ls,l,mid);
    build(t[now].rs,mid+1,r);
}

剩下的倍增lca的我就不说了。
还有就是这个题需要离散化,也很正常。
还有就是一个坑点,那个testcase是编号!不是组数!我被这个坑到10分。。。
剩下就是尽量优化优化常数,常数不要太大。
还要开够空间。
代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define ll long long
using namespace std;
inline int read(){
    int x=0;char ch=' ';int f=1;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct edge{
    int to,next;
}e[320001];
int T,n,m,q,tot;
int a[80001];
int fa[80001];
int son[80001];
int head[80001];
inline void addedge(int x,int y){
    e[++tot].to=y;e[tot].next=head[x];head[x]=tot;
}
struct Node{
    int size,ls,rs;
}t[80001*600];
int cnt;
int root[80001];
void build(int &now,int l,int r){
    now=++cnt;
    t[now].size=0;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(t[now].ls,l,mid);
    build(t[now].rs,mid+1,r);
}
void insert(int &now,int pre,int l,int r,int x){
    now=++cnt;
    t[now]=t[pre];
    t[now].size++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)insert(t[now].ls,t[pre].ls,l,mid,x);
    else insert(t[now].rs,t[pre].rs,mid+1,r,x);
}
int b[80001];
int size;
int query(int x,int y,int pre1,int pre2,int l,int r,int k){
    if(l==r)return b[l];
    int lsize=t[t[x].ls].size+t[t[y].ls].size-t[t[pre1].ls].size-t[t[pre2].ls].size;
    int mid=(l+r)>>1;
    if(k<=lsize)return query(t[x].ls,t[y].ls,t[pre1].ls,t[pre2].ls,l,mid,k);
    else return query(t[x].rs,t[y].rs,t[pre1].rs,t[pre2].rs,mid+1,r,k-lsize);
}
inline int Hash(int x){
    return lower_bound(b+1,b+size+1,x)-b;
}
int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
int st[80001][17];
int dep[80001];
int vis[80001];
void dfs(int x,int father,int rt){
    st[x][0]=father;
    for(int k=1;k<=16;k++){
        st[x][k]=st[st[x][k-1]][k-1];
    }
    son[rt]++;
    dep[x]=dep[father]+1;
    fa[x]=father;
    vis[x]=1;
    insert(root[x],root[father],1,size,Hash(a[x]));
    for(int i=head[x];i;i=e[i].next){
        int u=e[i].to;
        if(u==father)continue;
        dfs(u,x,rt);
    }
}
inline int getlca(int x,int y){
    if(x==y)return x;
    if(dep[x]>dep[y])swap(x,y);
    for(int k=16;k>=0;k--){
        if(dep[st[y][k]]>=dep[x]){
            y=st[y][k];
        }
    }
    if(x==y)return x;
    for(int k=16;k>=0;k--){
        if(st[x][k]!=st[y][k]){
            x=st[x][k];
            y=st[y][k];
        }
    }
    return st[x][0];
}
int main(){
    T=read();
    T=1;
    while(T--){
        memset(head,0,sizeof(head));
        memset(dep,0,sizeof(dep));
        memset(vis,0,sizeof(vis));
        memset(st,0,sizeof(st));
        memset(son,0,sizeof(son));
        tot=0;
        cnt=0;
        n=read();m=read();q=read();
        for(int i=1;i<=n;i++){
            a[i]=read();
            b[i]=a[i];
            fa[i]=i;
        }
        sort(b+1,b+n+1);
        size=unique(b+1,b+n+1)-b-1;
        for(int i=1;i<=m;i++){
            int x=read(),y=read();
            addedge(x,y);addedge(y,x);
        }
        build(root[0],1,size);
        for(int i=1;i<=n;i++){
            if(!vis[i]){
                dfs(i,0,i);
                fa[i]=i;
            }
        }
        int lastans=0;
        for(int i=1;i<=q;i++){
            char ch[3];
            int x,y,k;
            scanf("%s",ch);
            x=read()^lastans;
            y=read()^lastans;
            if(ch[0]=='Q'){
                k=read()^lastans;
                int lca=getlca(x,y);
                lastans=query(root[x],root[y],root[lca],root[st[lca][0]],1,size,k);
                printf("%d\n",lastans);
            }
            else{
                addedge(x,y);
                addedge(y,x);
                int u=find(x);
                int v=find(y);
                if(son[u]<son[v]){
                    swap(u,v);
                    swap(x,y);
                }
                dfs(y,x,u);
            }
        }
    }
    return 0;
}
0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

BZOJ 3123 [Sdoi2013]森林 主席树+启发式合并

BZOJ 3123 [Sdoi2013]森林 主席树+启发式合并
  • wzq_QwQ
  • wzq_QwQ
  • 2015-10-29 20:59
  • 1970

[BZOJ3123][Sdoi2013]森林(主席树启发式合并)

题目描述传送门题解主席树的启发式合并裸题? 坑点: ①强制在线,但是我刚开始的时候ans里存的并不是答案,而是离散化之后的值,gg。 ②合并的时候lca数组要完全清空。代码#include...
  • Clove_unique
  • Clove_unique
  • 2016-12-15 16:42
  • 302

[BZOJ3123][Sdoi2013]森林(主席树+启发式合并)

首先介绍下树上主席树: 在序列上的主席树,是对序列的每一个前缀都建立一个线段树版本。 而树上主席树,是对树上的每一个节点到根的路径都建立一个线段树版本。 而如果query(u)query(u)是...
  • xyz32768
  • xyz32768
  • 2018-01-01 19:44
  • 62

【bzoj3123】【sdoi2013】【森林】【启发式合并+主席树】

Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号。保证1≤testcase≤20。  第二行包含三个整数N,M,T,分别...
  • sunshinezff
  • sunshinezff
  • 2016-06-09 09:59
  • 200

[BZOJ3123][Sdoi2013]森林(主席树+启发式合并)

就因为ATP这篇题解的摘要一开始写了个“zyf2000的头发短得匪夷所思= =”,结果zyf2000竟然去给点了一个“踩”。。。。=n=
  • FromATP
  • FromATP
  • 2016-12-18 17:48
  • 498

bzoj3123 【SDOI2013】森林 启发式合并

Description小 Z有一片 森林 ,含有 N个节点, 每个 节点上都有一非负 整数作为 权值 。 初始的时候, 森林中有 M条边。小 Z希望 执行 T个操作 ,操作有两类:1、Q x y k ...
  • qq_35866453
  • qq_35866453
  • 2017-02-28 17:04
  • 124

【算法】树上启发式合并算法

树上启发式合并算法是启发式合并算法在树上的应用。下面我直接通过一个例子来讲解这个算法。         例:给定一棵有根树,树的结点编号为1~n,根结点为结点1。结点i有颜色col[i],其中1≤co...
  • ZJZNKU
  • ZJZNKU
  • 2017-03-25 17:22
  • 881

洛谷月赛 P3406 海底高铁

P3406 海底高铁 题目提供者kkksc03 标签 云端评测 难度 普及/提高- 题目背景 大东亚海底隧道连接着厦门、新北、博艾、那霸、鹿儿岛等城市,横穿东海,耗资10...
  • sinat_34550050
  • sinat_34550050
  • 2016-10-16 07:34
  • 366

洛谷 P1262 间谍网络

图的遍历。。。
  • Rlt1296
  • Rlt1296
  • 2016-07-05 18:16
  • 238

主席树/可持久化线段树简介(洛谷P3834/P3919)

又称函数式线段树,顾名思义,也就是通过函数来实现的线段树,至于为什么叫主席树,那是因为是fotile主席创建出来的这个数据结构
  • a1799342217
  • a1799342217
  • 2017-12-24 10:30
  • 226
    个人资料
    • 访问:26769次
    • 积分:2440
    • 等级:
    • 排名:第17535名
    • 原创:219篇
    • 转载:1篇
    • 译文:0篇
    • 评论:18条
    公告栏
    最新评论