一种黑科技:珂朵莉树

首先要明白的是:珂朵莉树(ODT)是一种用来骗分的暴力数据结构。

珂朵莉树的思想是利用集合set,把相同且连续的元素合并为一个个区间,从而进行区间修改;因此,珂朵莉树是区间的集合,这点可以通过定义结构体来实现。

珂朵莉树由两个核心操作构成:

1、split(分裂)

通过split,我们可以将一个区间分为两段,例如对于区间[l,r]中的位置possplit(pos)就是把区间分为[l,pos-1],[pos,r]两段。

split返回值是一个iterator,它对应的区间是[pos,r]

2、assign_val(区间推平)

通过split(l),split(r+1)(注意右端点分裂时要加一),可以提取任意区间[l,r],而区间推平就是把整段区间修改为某一个值,因此通过erase(split(l),split(r+1)),再insert(node(l,r,val))(其中node为区间对应的结构体,val为要修改的值),就可以实现区间推平操作。

详细的讲解可移步我的洛谷博客。关于定义珂朵莉树的方法和这些操作的具体实现方法,大家在网上应该可以搜到yzhang的博客和B站UESTC算法讲堂的视频讲解,因此我就不多赘述了。

但是,以上讲解的例题多数是一些专门为珂朵莉树设计的题,有没有在实际比赛中的应(pian)用(fen)案例呢?

确实有,这里我就找到了一题:[SCOI2010]序列操作

大家可以看到题解中的线段树标程从4K-6K不等,然后再看下珂朵莉树解法:

时间效率是不是看起来一点都不像暴力?

言归正传,为什么这题可以用珂朵莉树?可以看到题面中有如下操作:

0 a b 把[a, b]区间内的所有数全变成0

1 a b 把[a, b]区间内的所有数全变成1

这种需要将区间整段赋值的题,在数据随机的情况下,珂朵莉树的效率非常高。

于是我们尝试用珂朵莉树进行维护:对于操作0、1可以用珂朵莉树的区间推平实现,而操作2则利用split提取区间后依次取反,操作3、4也同理,利用split提取区间进行相应操作。

那么,上代码:

#include<cstdio>
#include<set>
using namespace std;
    struct node
    {
        int l,r;
        mutable bool v;
        node(int L,int R=-1,bool V=0)
        {
            l=L;
            r=R;
            v=V;
        };
        bool operator<(const node&next) const
        {
            return l<next.l;
        }
    };
    set<node> Chtholly_tree;
    int n,m;
set<node>::iterator split(int pos)
{
    set<node>::iterator it=Chtholly_tree.lower_bound(node(pos));
    if (it!=Chtholly_tree.end()&&it->l==pos) return it;
    it--;
    int left=it->l;
    int right=it->r;
    int temp=it->v;
    Chtholly_tree.erase(it);
    Chtholly_tree.insert(node(left,pos-1,temp));
    return Chtholly_tree.insert(node(pos,right,temp)).first;
}
void assign_val(int l,int r,int temp)
{
    set<node>::iterator itr=split(r+1);
    set<node>::iterator itl=split(l);
    Chtholly_tree.erase(itl,itr);
    Chtholly_tree.insert(node(l,r,temp));
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        int t;
        scanf("%d",&t);
        Chtholly_tree.insert(node(i,i,t));
    }
    Chtholly_tree.insert(node(n+1,n+1,0));
    for (int i=1;i<=m;i++)
    {
        int opt,a,b;
        scanf("%d%d%d",&opt,&a,&b);
        a++;
        b++;
        if (opt==0)
            assign_val(a,b,0);
        else if (opt==1)
            assign_val(a,b,1);
        else if (opt==2)
        {
            set<node>::iterator itr=split(b+1);
            set<node>::iterator itl=split(a);
            for (;itl!=itr;itl++) 
                itl->v=!itl->v;
        }
        else if (opt==3)
        {
            int ans=0;
            set<node>::iterator itr=split(b+1);
            set<node>::iterator itl=split(a);
            for (;itl!=itr;itl++)
                if (itl->v)
                    ans+=itl->r-itl->l+1;
            printf("%d\n",ans);
        }
        else if (opt==4)
        {
            int ans=0;
            int now=0;
            set<node>::iterator itr=split(b+1);
            set<node>::iterator itl=split(a);
            for (;itl!=itr;itl++)
                if (itl->v)
                    now+=itl->r-itl->l+1;
                else if (!itl->v)
                {
                    ans=max(ans,now);
                    now=0;
                }
            ans=max(ans,now);
            printf("%d\n",ans);
        }
        /*printf("\n");
        set<node>::iterator it=Chtholly_tree.begin();
        for (;it!=Chtholly_tree.end();it++)
            printf("%d %d %d\n",it->l,it->r,it->v);
        printf("\n");*/
    }
    return 0;
}

 

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值