码迷,mamicode.com
首页 > 其他好文 > 详细

启发式合并复习

时间:2017-03-31 01:08:23      阅读:172      评论:0      收藏:0      [点我收藏+]

标签:union   并查集   event   one   log   pac   scanf   target   algo   

T1 永无乡

初做:2017.3.8

http://www.cnblogs.com/TheRoadToTheGold/p/6520714.html

现在:2017.3.30

大约2个半小时

许多点,点有点权,2个操作:连边、询问与某个点相连的点权的k值

技术分享
#include<cstdio>
#include<algorithm>
#define N 100001
using namespace std;
int n,m,tot,cnt,fa[N];
int sum[N*20],lc[N*20],rc[N*20],root[N];
int key[N],hashh[N];
int find(int i) { return fa[i]==i ? i: fa[i]=find(fa[i]); }
void insert(int & now,int l,int r,int w)
{
    if(!now) now=++cnt;
    sum[now]++;
    if(l==r) return;
    int mid=l+r>>1;
    if(w<=mid) insert(lc[now],l,mid,w);
    else insert(rc[now],mid+1,r,w);
}
int query(int now,int l,int r,int w)
{
    if(l==r) return hashh[l];
    int mid=l+r>>1;
    if(w<=sum[lc[now]]) return query(lc[now],l,mid,w);
    else return query(rc[now],mid+1,r,w-sum[lc[now]]);
}
int unionn(int x,int y)
{
    if(!x) return y;
    if(!y) return x;
    lc[x]=unionn(lc[x],lc[y]);
    rc[x]=unionn(rc[x],rc[y]);
    sum[x]=sum[lc[x]]+sum[rc[x]];
    return x;
}
int main()
{
    scanf("%d%d",&n,&m); 
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&key[i]);
        fa[i]=i;
        hashh[key[i]]=i;
    }
    int x,y,r1,r2;
    while(m--)
    {
        scanf("%d%d",&x,&y);
        r1=find(x);r2=find(y);
        if(r1!=r2) fa[r2]=r1;
    }
    for(int i=1;i<=n;i++)
     insert(root[find(i)],1,n,key[i]);
    scanf("%d",&m);
    char c[1];
    while(m--)
    {
        scanf("%s%d%d",c,&x,&y);
        if(c[0]==B)
        {
            r1=find(x);r2=find(y);
            if(r1!=r2)
            {
                fa[r2]=r1;
                unionn(root[r1],root[r2]);
            }
        }
        else
        {
            x=find(x);
            if(y>sum[root[x]]) printf("-1\n");
            else printf("%d\n",query(root[x],1,n,y));
        }
    }
}
View Code

首先,自恃做过一遍不读完题,上来写了一个离散化点权

题目中写着点权1——n,所以不用离散化

 

顺手写的错误:

if(r1!=r2)
{
    fa[r2]=r1;
    unionn(r1,r2);
}

应该是

    unionn(root[r1],root[r2]);

并查集写太熟了,以后注意思考

 

本质错误:合并

错误合并1:

void unionn(int & x,int y)
{
    if(!y) return;
    if(!x) x=++cnt;
    sum[x]+=sum[y];
    unionn(lc[x],lc[y]);
    unionn(rc[x],rc[y]);
}

把y合并到x上,如果y为0,那么自y一下都不用合并,

如果x为0,建立新的x

分析:合并思路正确,代码也正确,但做了大量无用的合并

如果x为0,新建一系列节点,这些节点完全等于y以下的节点,所以与y公用即可

 

错误合并2:

void unionn(int & x,int y)
{
    if(!y) return;
    if(!x) x=y;
    else sum[x]+=sum[y];
    unionn(lc[x],lc[y]);
    unionn(rc[x],rc[y]);
}

思路错误,错误原因还不清楚

 

正确合并:

int unionn(int x,int y)
{
    if(!x) return y;
    if(!y) return x;
    lc[x]=unionn(lc[x],lc[y]);
    rc[x]=unionn(rc[x],rc[y]);
    sum[x]=sum[lc[x]]+sum[rc[x]];
    return x;
}

自底向上的合并,公用节点

 

恕我无能,splay的没调出来

 

启发式合并复习

标签:union   并查集   event   one   log   pac   scanf   target   algo   

原文地址:http://www.cnblogs.com/TheRoadToTheGold/p/6648986.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!