zoukankan      html  css  js  c++  java
  • [bzoj3123][Sdoi2013]森林_主席树_启发式合并

    森林 bzoj-3123 Sdoi-2013

    题目大意:给定一片共n个点的森林,T个操作,支持:连接两个不在一棵树上的两个点;查询一棵树上路径k小值。

    注释:$1le n,T le 8cdot 10^4$

    想法:运用冯老师讲的方法:

    “对于一个开起来非常困难的问题,我们可以通过先构造拟对象,然后向完全对象转化”

    这个题,我们看到了最后一个操作,想到了主席树。

    两个点x、y之间的路径,我们在主席树上用root[x]+root[y]-root[lca(x,y)]-root[fa[lca(x,y)]]的权值线段树即可。

    那么,如果加上连接操作呢?

    直接启发式合并,然后重置一部分的主席树即可。(重置的时候记得把倍增的数组也重置)

    最后,附上丑陋的代码... ...

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #include <map>
    using namespace std;
    int cnt,tot,ans,len,a,b,tcase,n,m,q;
    char s[5];
    int x,y,z,d[80010],h[80010],v[80010];
    map<int,int>g;
    int to[200010],vis[80010],anc[80010],size[80010],ls[20000010],rs[20000010],nxt[200010],head[200010],f[80010][26],sum[20000010],root[2000010];
    inline void add(int x,int y)
    {
    	to[++tot]=y;
    	nxt[tot]=head[x];
    	head[x]=tot;
    }
    int lca(int x,int y)
    {
        if(d[x]<d[y])
        {
            swap(x,y);
        }
        int dep=d[x]-d[y];
        for(int i=0;i<=23;i++)
        {
            if((dep&(1<<i))!=0)
            {
                x=f[x][i];
            }
        }
        if(x==y)
        {
            return x;
        }
        for(int i=23;i>=0;i--)
        {
            if(f[x][i]!=f[y][i])
            {
                x=f[x][i];
                y=f[y][i];
            }
        }
        return f[x][0];
    }
    int updata(int pre,int l,int r,int v)
    {
        int pos=++cnt,mid=(l+r)>>1;
        ls[pos]=ls[pre];
        rs[pos]=rs[pre];
        sum[pos]=sum[pre]+1;
        if(l==r)
        {
            return pos;
        }
        else
        {
            if(v<=mid)
            {
                ls[pos]=updata(ls[pre],l,mid,v);
            }
            else
            {
                rs[pos]=updata(rs[pre],mid+1,r,v);
            }
        }
        return pos;
    }
    int query(int x,int y,int fa,int anc,int l,int r,int k)
    {
    	int mid=(l+r)>>1;
        if(l==r)
        {
            return g[l];
        }
        int num=sum[ls[x]]+sum[ls[y]]-sum[ls[fa]]-sum[ls[anc]];
        if(k<=num)
        {
            return query(ls[x],ls[y],ls[fa],ls[anc],l,mid,k);
        }
        else
        {
            return query(rs[x],rs[y],rs[fa],rs[anc],mid+1,r,k-num);
        }
    }
    void dfs(int x,int fa,int ac)
    {
        vis[x]=1;
        anc[x]=ac;
        f[x][0]=fa;
        d[x]=d[fa]+1;
        root[x]=updata(root[fa],1,n,v[x]);
        for(int i=1;i<=23;i++)
        {
            f[x][i]=f[f[x][i-1]][i-1];
        }
        for(int i=head[x];i;i=nxt[i])
        {
            if(to[i]!=fa)
            {
                dfs(to[i],x,ac);
                size[x]+=size[to[i]];
            }
        }
    }
    int main()
    {
        scanf("%d",&tcase);
        scanf("%d%d%d",&n,&m,&q);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&v[i]);
            h[i]=v[i];
            size[i]=1;
        }
        sort(h+1,h+1+n);
        len=unique(h+1,h+1+n)-h-1;
        for(int i=1;i<=n;i++)
        {
            int val=v[i];
            v[i]=lower_bound(h+1,h+1+len,v[i])-h;
            g[v[i]]=val;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        for(int i=1;i<=n;i++)
        {
            if(vis[i]) continue;
            dfs(i,0,i);
        }
        while(q--)
        {
            scanf("%s",s);
            if(s[0]=='Q')
            {
                scanf("%d%d%d",&x,&y,&z);
                x=x^ans;
                y=y^ans;
                z=z^ans;
                a=lca(x,y);
                b=f[a][0];
                ans=query(root[x],root[y],root[a],root[b],1,n,z);
                printf("%d
    ",ans);
            }
            else
            {
                scanf("%d%d",&x,&y);
                x=x^ans;
                y=y^ans;
                add(x,y);
                add(y,x);
                if(size[anc[x]]<size[anc[y]])
                {
                    size[anc[y]]+=size[anc[x]];
                    dfs(x,y,anc[y]);
                }
                else
                {
                    size[anc[x]]+=size[anc[y]];
                    dfs(y,x,anc[x]);
                }
            }
        }
    }
    

    小结:主席树活用起来真tm吓人... ...

  • 相关阅读:
    TyporaRecord
    c# 串口 应答式顺序下发命令 循环 间隔发送指令
    WPF 如何在单独的配置文件中使用Log4net
    UWP VisualStateManager
    USB通信
    UWP RelativePanel
    JSON 序列化与反序列化
    Unity 依赖注入的三种常用模板
    IOC Unity容器的基本使用
    使用EF完成基于SQLite的CodeFirst
  • 原文地址:https://www.cnblogs.com/ShuraK/p/9362749.html
Copyright © 2011-2022 走看看