福州日志4

       时间过得很快,这段日子已经走完了一半,照例做一个今天的小结和知识整理。

今天讲了一些算法,数量不多,难度却是有的,所以接下来的每个算法都是详细讲述的。

                                                                                frist.并查集

       所谓的并查集就是一种类似于小组的东西,被称为集合,且不同的集合可以进行合并。可用于处理合并集合和查找集合的算法,即将数据作为集合储存,有一个代表元作为这个集合的总的父亲节点,同时,这个点也是自己的父亲节点。

       作为并查集,肯定要具备并,查,集,等特点,下面将其一一实现

        1.“集”就是这个算法是用来处理集合问题的;我们可以先将每个数都各自看作是一个集合,由此来实现初始化,代码如下:

for(int i = 1 ; i <= n ; ++i)
father[i] = i; 

        2.“并”就是可以按需要将属于同一组的元素所在的集合合并;其实只是将其代表元做了改变,代码如下:

int findFather(int x){
if(father[x] == x)return x;
else return findFather(father[x]);
}

        3.“查”就是查询元素所在集合,查询不同元素、不同集合之间的关系。实现代码如下:

int fu , fv;
fu = findFather(u);
fv = findFather(v);
father[fu] = fv;

                                                                                 second.树状数组

    树状数组用于频繁的求和操作,而且速度快,易于改变数据量。它是利用跳跃存储来实现的,跳跃大小定义为

lowbit(x)(即x的二进制表示,仅保留最低位1得到的数字)

若用代码实现可写为:

int lowbit(x){
return (x & (-x));
}

要实现求前缀和,还要快,就是利用这个跳跃,然后完成树状数组,代码实现如下:

int getsum(int k){
int sum = 0;
while(k > 0)
{
    sum += star[k];
    k -= lowbit(k);
    }return sum;
} 

    然后就是动态修改,修改时只需把增值同步到位置上即可,如代码所示:

void modify(int x , int delta){
        while(x <= N){
        star[x] += delta;
        x += lowbit(x);
    }
}

这样便实现了树状数组;

third.线段树

常用于求区间特征值,可以快速完成查找和修改;

1.建树,有树才能用,想解题,先建树,可通过递归建树,代码如下:

void build(int id , int l , int r)

{

    tree[id].left=l; tree[id].right=r;
    if (l==r){
        tree[id].sum=a[l];
        tree[id].max=a[l];
    }
    else{
        int mid=(l+r)/2;
        build(id*2,l,mid);
        build(id*2+1,mid+1,r);
        tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
        tree[id].max=max(tree[id*2].max,tree[id*2+1].max;
    }
} 

2.查找,完成了建树就要进行利用,我们可以通过区间查找去寻找区间特征值,用代码实现如下:


3
.单个数据的修改,为了实现频繁的查找和改变,我们可以利用回溯对数据进行更新,代码如下:

int query(int id , int l , int r){
    if (tree[id].left==l&&tree[id].right==r)
        return tree[id].sum; //询问总和,可改为最值
    }
    else{
        int mid=(tree[id].left+tree[id].right)/2;
        if (r<=mid) return query(id*2,l,r);
        else
            if (l>mid) return query(id*2+1,l,r)
            else
                return query(id*2,l,mid)+query(id*2+1,mid+1,r);
            }
}

void update(int id , int pos , int val){
if (tree[id].left==tree[id].right){
tree[id].sum=tree[id].max=val;
}
else{
int mid=(tree[id].left+tree[id].right)/2;
if (pos<=mid) update(id*2,pos,val);
else update(id*2+1,pos,val);
tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
tree[id].max=max(tree[id*2].max,tree[id*2+1].max)
}
}

4.区间的数据更新,因为效率问题,我们肯定不能用上面的代码重复多次来实现,因此,我们要给tree增加一个

标记,并对查找做出改变,来一段代码深入理解一下:

void pushdown(int id){
    if(tree[id].tag == true){
        tree[id*2].tag = tree[id*2 + 1].tag = true; //下传标记到左右儿子
        tree[id*2].delta += tree[id].delta; //下传变动记录到左儿子,以便左儿子继续下传
        tree[id*2].max += tree[id].delta; //更改左儿子数值
        tree[id*2].sum += (tree[id*2].r – tree[id*2].l + 1) * tree[id].delta;
        tree[id*2 + 1].delta += tree[id].delta; //下传变动记录到右儿子,以便右儿子继续下传
        tree[id*2 + 1].max += tree[id].delta; //更改右儿子数值
        tree[id*2 + 1].sum += (tree[id*2 + 1].r – tree[id*2 + 1].l + 1) * tree[id].delta;
        tree[id].tag = false; //儿子已被修改,清空标记
        tree[id].delta = 0;
    }
} 



void query(int id , int l , int r){
    if (tree[id].left==l&&tree[id].right==r){
    return tree[id].sum;
    }
    else{
        pushdown(id); // 不得不访问儿子了,就要下传
        int mid=(tree[id].left+tree[id].right)/2;
        if (r<=mid) return query(id*2,l,r);
        else
            if (l>mid) return query(id*2+1,l,r)
            else
                return query(id*2,l,mid) +
        query(id*2+1,mid+1,r);
    }
}

void modify(int id , int l , int r , int x){
    if (tree[id].left>=l && tree[id].right<=r)
    tree[id].max+=x;
    tree[id].sum+=x*(tree[id].r-tree[id].l+1);// 标记儿子需要修改但尚未修改
    tree[id].tag = true; tree[id].delta = x;
·    }

    else{

        pushdown(id); // 不得不访问儿子了,就要下 传

        int mid=(tree[id].left+tree[id].right)/2;

        if (r<=mid) modify(id*2,l,r);
        else
        if (l>mid) modify(id*2+1,l,r)
        else{
            modify(id*2,l,mid);
            modify(id*2+1,mid+1,r);
        }
    tree[id].sum=
tree[id*2].sum+tree[id*2+1].sum;
    tree[id].max=max(tree[id*2].max,tree[id*2+1].max);
    }
}

这样就完成了线段树的操作;

                                                                                      END.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值