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

原创 2017年10月06日 21:38:45

传送门(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;
}
版权声明:如有人转载,请为我这个苦逼的OIer注明出处吧^_^

相关文章推荐

洛谷10月月赛R1-T2-一道简单题 Sequence2

传送门 这个题明显是dp,我们来考虑如何dp。 我们把四种状态:(本列)选1,选2,选3(大于等于上一个),选3(小于等于上一个) 分别称为:状态1,状态2,状态3,状态4 那么,我们可以发现...

Codeforces 296D Greg and Graph【Floyd+逆序思维】好题

D. Greg and Graph time limit per test 3 seconds memory limit per test 256 megabytes input...

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

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

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

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

bzoj3123 森林 主席树&启发式合并

如果没有Link操作就是裸的主席树。        如果有Link操作,就每次把小的往大的并,每个点最多并logN次,每次logN,因此总的时间复杂度O(Nlog^2N),空间复杂度O(Nlog^2N...

BZOJ 3123 SDOI 2013 森林 可持久化线段树+启发式合并

题目大意:给出一个森林,每个节点都有一个权值。有若干加边操作,问两点之间路径上的第k小权值是多少。 思路:这题和COT1比较像,但是多了连接操作。这样就只能暴力合并连个树。启发式合并会保证时...

BZOJ 3123 主席树 启发式合并

思路: 主席树 搞树上的k大 x+y-lca(x,y)-fa(lca(x,y)) 按照size小树往大树上插 启发式合并 n*log^2n的 搞定~//By SiriusRen #inclu...

[BZOJ2809][Apio2012]dispatching(枚举&dfs序+主席树||可并堆||splay+启发式合并)

你要堕落,神仙也救不了; 你要成长,绝处也能逢生。

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

题目大意:给定一棵树,每个节点有权值,询问两个节点路径上的权值第k小 这题很卡时间。。。 树链剖分+二分+树套树的O(nlog^4n)做法可以去死了 没有修改操作,树链剖分+二分+划分树O(nl...

[BZOJ4539][Hnoi2016]树(倍增+LCA+主席树)

这篇题解ATP竟然码了三天。。
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)