zoukankan      html  css  js  c++  java
  • 【BZOJ3123】森林(SDOI2013)-树上主席树+启发式合并

    测试地址:森林
    做法:本题需要用到树上主席树+启发式合并。
    如果树的形态固定,那么求路径第k大,我们想到用主席树。求区间的第k大我们是在序列上建主席树,每棵线段树代表一个前缀和,那么求路径的第k大,类比序列的前缀和与树上前缀和的转化,我们可以在树上建主席树,每棵线段树代表从一个点到根路径上的信息,这样我们就可以通过x+ylca(x,y)fa(lca(x,y))这样的形式来得到某条路径的信息了,时间复杂度为O(nlogn)
    现在的问题是,要求动态连边,还强制在线,我们想到LCT,但是LCT上的信息需要用splay而非线段树或主席树来维护,不能可持久化。于是我们退一步,既然O(nlogn)不能做到,那我们就放慢其中的一个操作,使得这两个操作不互斥。
    显然应该放慢的是连边的操作。注意到只有连边,所以使用启发式合并,每次合并暴力重构较小的树上的主席树和要求LCA所需的倍增信息,这样就可以做到O(nlog2n)的总时间复杂度了,于是我们就解决了此题。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,t,tot,tmp,pos[80010];
    int first[80010]={0},totedge=0,rt[80010]={0};
    int val[80010],fa[80010][21]={0},dep[80010]={0},top[80010],siz[80010];
    int seg[10000010]={0},ch[10000010][2]={0};
    struct forsort
    {
        int id,val;
    }f[80010];
    struct edge
    {
        int v,next;
    }e[200010];
    
    bool cmp(forsort a,forsort b)
    {
        return a.val<b.val;
    }
    
    void insert(int a,int b)
    {
        e[++totedge].v=b;
        e[totedge].next=first[a];
        first[a]=totedge;
    }
    
    int find(int x)
    {
        int r=x,i=x,j;
        while(r!=top[r]) r=top[r];
        while(i!=r) j=top[i],top[i]=r,i=j;
        return r;
    }
    
    void merge(int x,int y)
    {
        int fx=find(x),fy=find(y);
        top[fx]=fy;
        siz[fy]+=siz[fx];
    }
    
    void buildtree(int &v,int l,int r)
    {
        v=++tot;
        ch[v][0]=ch[v][1]=0;
        seg[v]=0;
        if (l==r) return;
        int mid=(l+r)>>1;
        buildtree(ch[v][0],l,mid);
        buildtree(ch[v][1],mid+1,r);
    }
    
    void add(int &v,int last,int l,int r,int x)
    {
        v=++tot;
        ch[v][0]=ch[last][0];
        ch[v][1]=ch[last][1];
        seg[v]=seg[last];
        if (l==r)
        {
            seg[v]++;
            return;
        }
        int mid=(l+r)>>1;
        if (x<=mid) add(ch[v][0],ch[last][0],l,mid,x);
        else add(ch[v][1],ch[last][1],mid+1,r,x);
        seg[v]=seg[ch[v][0]]+seg[ch[v][1]];
    }
    
    int query(int a,int b,int c,int d,int l,int r,int k)
    {
        if (l==r) return l;
        int mid=(l+r)>>1;
        if (seg[ch[a][0]]+seg[ch[b][0]]-seg[ch[c][0]]-seg[ch[d][0]]<k)
        {
            k-=seg[ch[a][0]]+seg[ch[b][0]]-seg[ch[c][0]]-seg[ch[d][0]];
            return query(ch[a][1],ch[b][1],ch[c][1],ch[d][1],mid+1,r,k);
        }
        else return query(ch[a][0],ch[b][0],ch[c][0],ch[d][0],l,mid,k);
    }
    
    void rebuild(int v,int Fa)
    {
        for(int i=1;i<=18;i++)
            fa[v][i]=fa[fa[v][i-1]][i-1];
        for(int i=first[v];i;i=e[i].next)
            if (e[i].v!=Fa)
            {
                fa[e[i].v][0]=v;
                dep[e[i].v]=dep[v]+1;
                rt[e[i].v]=0;
                add(rt[e[i].v],rt[v],1,tmp,val[e[i].v]);
                rebuild(e[i].v,v);
            }
    }
    
    void link(int x,int y)
    {
        int fx=find(x),fy=find(y);
        if (siz[fx]>siz[fy]) swap(x,y);
    
        fa[x][0]=y;
        dep[x]=dep[y]+1;
        rt[x]=0;
        add(rt[x],rt[y],1,tmp,val[x]);
        rebuild(x,0);
    
        merge(x,y);
        insert(x,y),insert(y,x);
    }
    
    int lca(int x,int y)
    {
        if (dep[x]<dep[y]) swap(x,y);
        for(int i=18;i>=0;i--)
            if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
        if (x==y) return x;
        for(int i=18;i>=0;i--)
            if (fa[x][i]!=fa[y][i])
                x=fa[x][i],y=fa[y][i];
        return fa[x][0];
    }
    
    int main()
    {
        scanf("%d",&t);
        scanf("%d%d%d",&n,&m,&t);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&f[i].val);
            f[i].id=i;
        }
        sort(f+1,f+n+1,cmp);
    
        tmp=0;
        for(int i=1;i<=n;i++)
        {
            if (i==1||f[i].val!=f[i-1].val)
                pos[++tmp]=f[i].val;
            val[f[i].id]=tmp;
        }
    
        tot=0;
        buildtree(rt[0],1,tmp);
        dep[0]=-1;
        for(int i=1;i<=n;i++)
        {
            top[i]=i;
            siz[i]=1;
            add(rt[i],rt[0],1,tmp,val[i]);
        }
    
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            link(a,b);
        }
    
        int lastans=0;
        for(int i=1;i<=t;i++)
        {
            char s[3];
            int x,y,k;
            scanf("%s",s);
            if (s[0]=='Q')
            {
                scanf("%d%d%d",&x,&y,&k);
                x^=lastans,y^=lastans,k^=lastans;
                printf("%d
    ",lastans=pos[query(rt[x],rt[y],rt[lca(x,y)],rt[fa[lca(x,y)][0]],1,tmp,k)]);
            }
            else
            {
                scanf("%d%d",&x,&y);
                x^=lastans,y^=lastans;
                link(x,y);
            }
        }
    
        return 0;
    }
  • 相关阅读:
    图解HTTPS
    JQuery 控件
    sql server 中某个字段值合并【转】
    ASP.NET时间函数及其格式转换
    数据库 'tempdb' 的日志已满
    @@ERROR 和 @@ROWCOUNT
    SQL Server中行列转换 Pivot UnPivot 【转】
    Global.asax详解
    SQL Server 2008时提示评估期已过的解决办法
    C# IO读取文件问题:正由另一进程使用
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793347.html
Copyright © 2011-2022 走看看