• Old Driver Tree(ODT 老驱动树)


    ODTODT,中文称为老驱动树,又名珂朵莉树(虽然我看到老驱动莫名想笑)

    ODTODT真是一个暴力神奇的东西

    但是只有在数据随机且有区间覆盖操作的时候才有用(因为只有区间覆盖才会容易产生一段相同的区间)

    因为考虑到每次覆盖了一个区间之后整个区间的数都是一样的了

    于是就将这个区间缩成一个点,用setset维护一下所有点

    这时候我们就可以方便的对其操作

    具体实现:


    初始化

    考虑到每个点都是一段相同的区间

    所以要维护区间左右端点和区间的值

    用结构体实现

    #define IT set<node>::iterator
    #define pi pair<ll,int>
    #define mp(x,y) make_pair(x,y)
    struct node{
    	int l,r;
    	mutable ll val;
    	node(int L,int R=-1,ll v=0):l(L),r(R),val(v){}
    	bool operator < (const  node &a)const {return l<a.l;}
    };
    

    其主要操作就22个:

    看代码应该很好理解

    Split:Split: 将一段区间分成两段并返回后一段的开头,用作分离出一段特定区间来处理询问

    inline IT split(int pos){
    	IT it=st.lower_bound(node(pos));
    	if(it!=st.end()&&it->l==pos)return it;
    	it--;
    	int l=it->l,r=it->r;
    	ll val=it->val;
    	st.erase(it);
    	st.insert(node(l,pos-1,val));
    	return st.insert(node(pos,r,val)).first;
    }
    

    AssignAssign:用于处理区间覆盖,将一段区间提出来缩成一个点

    inline void assign(int l,int r,ll val){
    	IT itr=split(r+1),itl=split(l);
    	st.erase(itl,itr);
    	st.insert(node(l,r,val));	
    }
    

    另外几个操作(以CodeForces-896C为例)

    AddAdd区间加:直接遍历setsetll~rr中的所有点

    inline void add(int l,int r,int val){
    	IT itr=split(r+1),itl=split(l);
    	for(;itl!=itr;itl++)itl->val+=val;
    }
    

    KthKth查询区间第kk小:将区间所有点的信息(l,r,val)(l,r,val)加到一个vectorvector

    按大小和数量遍历到第kk个输出就是了

    inline ll kth(int l,int r,int k){
    	vector<pi>vec;
    	IT itr=split(r+1),itl=split(l);
    	for(;itl!=itr;itl++)
    	vec.push_back(mp(itl->val,itl->r-itl->l+1));
    	sort(vec.begin(),vec.end());
    	for(vector<pi>::iterator it=vec.begin();it!=vec.end();it++){
    		k-=it->second;
    		if(k<=0)return it->first;
    	}
    	return -1;
    }
    

    SumSum:区间所有数的kk次方之和

    inline ll ksm(ll a,ll b,ll mod){
    	int res=1;a%=mod;
    	for(;b;b>>=1,a=a*a%mod){
    		if(b&1)res=res*a%mod;
    	}
    	return res;
    }
    inline ll sum(int l,int r,ll x,ll y){
        ll res=0;
        IT itr=split(r+1),itl=split(l);
        for(;itl!=itr;++itl)
        res+=(ksm(itl->val,x,y)*((itl->r-itl->l+1)%y))%y,res%=y;
        return res;
    }
    

    感性理解一下复杂度 网上找不到证明

    设区间数为mm=nm,初始m=n

    由初中数学可以证明每次AssignAssign的期望长度约为 13frac13

    不明白的可以自己百度

    所以每次AssignAssign会使mm变成23mfrac23m并有概率产生2个新的区间

    开始的时候相当于每次减少13frac 13

    后面由于mm少了产生的新区间也会有贡献

    然后大概就是O(nlogn)O(nlogn)了…

    (赶紧逃)

  • 相关阅读:
    Spring之Redis访问(Spring-data-redis)
    SpringCloud之Hystrix容错保护原理及配置
    SpringCloud之Feign声明式调用原理及配置
    SpringCloud之Ribbon负载均衡配置
    基于Zookeeper实现分布式锁
    SpringCloud之Eureka注册中心原理及其搭建
    SpringBoot定时任务(schedule、quartz)
    Java和操作系统交互(Java 代码是怎么执行)(转)
    深入SpringBoot注解原理及使用
    Spring事务的配置、参数详情及其原理介绍(Transactional)
  • 原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366347.html
Copyright © 2020-2023  润新知