ODT珂朵莉树「建议收藏」

ODT珂朵莉树「建议收藏」珂朵莉树の由来珂朵莉树(或称ODT(OldDriverTree老司机树))这毒瘤算法由CodeForces-896CWillem,ChthollyandSeniorious的正解衍化而来由于其骗分暴力的非正统算法思想虽然很多时候在随机数据下跑时不错但切记这只是骗分暴力,时间复杂度上并不正确什么时候用珂朵莉树珂朵莉树一般用来解决本来应当由线段树解决的区间类问题而使…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

珂朵莉树の由来

珂朵莉树(或称ODT(Old Driver Tree老司机树))
这个毒瘤算法由CodeForces – 896C Willem, Chtholly and Seniorious 的正解衍化而来

虽然很多时候在随机数据下跑时不错
由于其骗分暴力的非正统算法思想
一定要切记这只是骗分暴力时间复杂度上并不正确


什么时候用珂朵莉树

珂朵莉树一般用来解决本来应当由线段树解决的区间类问题

而使得珂朵莉树暴力艹标程的关键是assign区间推平操作
也就是题目中必须有区间赋值操作,而且数据纯随机的情况下才有复杂度保证


珂朵莉树–初始化

一般珂朵莉树选择使用 set 维护数列
set中每个结点维护三个值 ( l l , r r , v a l ) (ll,rr,val) (ll,rr,val),意为 [ l l , r r ] [ll,rr] [ll,rr]内的值都为 v a l val val
也就是每个节点保存一段连续的值相同的区间
set中以每个区间左端点排序

struct node
{ 
   
    int ll,rr;
    mutable int val;
    node(int L,int R=-1,int V=0): ll(L), rr(R), val(V) { 
   }
    bool operator < (const node& tt)const { 
    return ll<tt.ll;}//以区间左端点排序
};
set<node> st;

需要注意的是 m u t a b l e mutable mutable
没有它对 v a l val val的修饰,在接下来add函数里导致CE。

接下来为方便有关迭代器的操作,我们做一个宏定义

#define IT set<node>::iterator

珂朵莉树–分裂Split

s p l i t ( p o s ) split(pos) split(pos)操作是指将原来含有pos位置的节点分成两部分 [ l , p o s − 1 ] [l,pos-1] [l,pos1] [ p o s , r ] [pos,r] [pos,r]
并返回分裂后以 p o s pos pos为左端点的结点的迭代器

IT split(int pos)
{ 
   
    IT it=st.lower_bound(node(pos));//二分找到第一个左端点不小于pos的区间
    if(it!=st.end()&&it->ll==pos) return it;//pos本身就是某个区间的左端点,不用分裂
    --it;//否则上一个区间才是包含pos的区间
    int ll=it->ll,rr=it->rr,val=it->val;
    st.erase(it);//删除原结点
    st.insert(node(ll,pos-1,val));
    return st.insert(node(pos,rr,val)).first;//这里.first返回的是迭代器
}

珂朵莉树–区间赋值Assign

珂朵莉树就是靠这个东西维持其不正确的复杂度的

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

分裂出需要的区间,删除后重新插入一个新的
注意分裂出 [ l l , r r ] [ll,rr] [ll,rr]区间时要先分裂右端点,在分裂左端点

e r a s e erase erase方法可以删除迭代器描述的区间 [ f i r s t , l a s t ) [first,last) [first,last),注意左闭右开

void erase (iterator first, iterator last)

数据纯随机的情况下,可以证明每次 a s s i g n assign assign区间长度期望 N / 3 N/3 N/3
于是 s e t set set规模迅速下降,随后达到接近 O ( m l o g n ) O(mlogn) O(mlogn)玄学非正确复杂度


珂朵莉树–其他暴力操作

其他操作真的就是规规矩矩的纯暴力
直接取出对应区间,暴力对每一个进行操作

一般就是长这样

void fun(int ll,int rr)
{ 
   
    IT itr=split(rr+1),itl=split(ll);
    for(;itl!=itr;++itl) 
    { 
   
    	//...
    }
}
煮个栗子–区间求和
int qsum(int ll,int rr)
{ 
   
    int res=0;
    IT itr=split(rr+1),itl=split(ll);
    for(;itl!=itr;++itl) res+=(itl->rr-itl->ll+1)*itl->val;//注意乘(itl->rr-itl->ll+1)
    return res;
}
再煮个复杂点的栗子–区间Kth

也是一样的暴力

#define pir pair<int,int>//前一个记录值,后一个记录出现次数
int kth(int ll,int rr,int k)
{ 
   
    vector<pir> vec;
    IT itr=split(rr+1),itl=split(ll);
    
    for(;itl!=itr;++itl)
    vec.push_back( pir(itl->val,itl->rr-itl->ll+1) );
    sort(vec.begin(),vec.end());//全部存下来排序就好
    
    for(vector<pir>::iterator it=vec.begin();it!=vec.end();++it)
    { 
   
        k-=it->second;
        if(k<=0) return it->first;
    }
    return -1;
}

珂朵莉树–暴力AC应用

CodeForces – 896C Willem, Chtholly and Seniorious

毒瘤ODT的源头

#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
#include<cstdio>
#include<set>
using namespace std;
typedef long long lt;
#define IT set<node>::iterator
#define pir pair<lt,int>
#define mkp(x,y) make_pair(x,y)

lt read()
{ 
   
    lt f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){ 
   if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){ 
   x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

lt seed,vmax;
lt rnd()
{ 
   
    lt res=seed;
    seed=(seed*7+13)%1000000007;
    return res;
}

const int maxn=100010;
int n,m;
lt a[maxn];
struct node
{ 
   
    int ll,rr;
    mutable lt val;
    node(int L,int R=-1,lt V=0): ll(L), rr(R), val(V) { 
   }
    bool operator < (const node& tt)const { 
    return ll<tt.ll;}
};
set<node> st;

lt qpow(lt a,lt k,lt p)
{ 
   
    lt res=1; a%=p;
    while(k>0){ 
   
        if(k&1) res=(res*a)%p;
        a=(a*a)%p; k>>=1;
    }
    return res;
}

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

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

void add(int ll,int rr,lt val)
{ 
   
    IT itr=split(rr+1),itl=split(ll);
    for(;itl!=itr;++itl) itl->val+=val;
}

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

lt qsum(int ll,int rr,lt x,lt y)
{ 
   
    lt res=0;
    IT itr=split(rr+1),itl=split(ll);
    for(;itl!=itr;++itl)
    res+=(qpow(itl->val,x,y)*((itl->rr-itl->ll+1)%y))%y,res%=y;
    return res;
}

int main()
{ 
   
    n=read();m=read();
    seed=read();vmax=read();
    
    for(int i=1;i<=n;++i)
    { 
   
        a[i]=(rnd()%vmax)+1;
        st.insert(node(i,i,a[i]));
    }
    
    for(int i=1;i<=m;++i)
    { 
   
        int op=(rnd()%4)+1;
        int ll=(rnd()%n)+1,rr=(rnd()%n)+1;
        lt x,y;

    	if(ll>rr) swap(ll,rr);
    	if(op==3) x=(rnd()%(rr-ll+1))+1;
    	else x=(rnd()%vmax)+1;
    	if(op==4) y=(rnd()%vmax)+1;
    	
    	if(op==1) add(ll,rr,x);
    	else if(op==2) assign(ll,rr,x);
    	else if(op==3) printf("%lld\n",kth(ll,rr,x));
    	else if(op==4) printf("%lld\n",qsum(ll,rr,x,y));
    }
    return 0;
}

洛谷P2572 [SCOI2010]序列操作

没什么好解释的。。毕竟就是暴力

#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
#include<set>
using namespace std;
#define IT set<node>::iterator

int read()
{ 
   
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){ 
   if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){ 
   x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=100010;
int n,m;
int a[maxn];
struct node
{ 
   
    int ll,rr;
    mutable int val;
    node(int L,int R=-1,int V=0): ll(L), rr(R), val(V) { 
   }
    bool operator < (const node& tt)const { 
    return ll<tt.ll;}
};
set<node> st;

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

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

void rev(int ll,int rr)
{ 
   
    IT itr=split(rr+1),itl=split(ll);
    for(;itl!=itr;++itl) itl->val^=1;
}

int qsum(int ll,int rr)
{ 
   
    int res=0;
    IT itr=split(rr+1),itl=split(ll);
    for(;itl!=itr;++itl) res+=(itl->rr-itl->ll+1)*itl->val;
    return res;
}

int qmax(int ll,int rr)
{ 
   
    int res=0,tt=0;
    IT itr=split(rr+1),itl=split(ll);
    for(;itl!=itr;++itl) 
    { 
   
        if(itl->val==0) res=max(res,tt),tt=0;
        else tt+=itl->rr-itl->ll+1;
    }
    return max(res,tt);
}

int main()
{ 
   
    n=read();m=read();
    for(int i=1;i<=n;++i)
    { 
   
        a[i]=read();
        st.insert(node(i,i,a[i]));
    }
    
    for(int i=1;i<=m;++i)
    { 
   
        int k=read(),ll=read()+1,rr=read()+1;
    	if(k==0) assign(ll,rr,0);
    	else if(k==1) assign(ll,rr,1);
    	else if(k==2) rev(ll,rr);
    	else if(k==3) printf("%lld\n",qsum(ll,rr));
    	else if(k==4) printf("%lld\n",qmax(ll,rr));
    }
    return 0;
}

洛谷P2787 语文1(chin1)- 理理思维 题解

时空消耗量都艹过正解不止一个数量级


再次提醒

不管珂朵莉树才实际中再怎么优秀,那都只是数据随机的结果
说到底只是暴力骗分手段
尽管可以用ODT AC很多题,但改变不了时间复杂度是非正确的的事实

建议平时的练习中少用,因为这样会忽略了题目本身正解的精髓
考场上对题目没有思路倒是一个很好的骗分手段

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/195468.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • ideaIU-2019.2.exe

    一、查看安装目录结构   bin: 容器,执行文件和启动参数等 help:快捷键文档和其他帮助文档 jbr: 含有java运行环境 lib:idea 依赖的类库 license:各个插件许可

  • html+css网页开发 之 头部导航条(logo、导航栏、搜索框)

    html+css网页开发 之 头部导航条(logo、导航栏、搜索框)页面布局整体思路:确定页面的版心(可视区),测量可知。 分析页面中的行模块,以及每个行模块中的列模块。 一行中列模块常用浮动布局,先确定每个列的大小,之后确定列的位置。 制作HTML结构。遵循先有结构,后有样式的原则。头部制作1号是版心盒子header1200*42的盒子水平居中对齐 版心盒子内包含2号盒子logo 版心盒子内包含3号盒子nav导航栏 版心盒子内包含4号盒子search搜索框 版心盒子内包含5号盒子user个人信息 注意4个盒子都必须是浮动style.c..

  • webstorm2021激活码_通用破解码

    webstorm2021激活码_通用破解码,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • VirtualBox安装Mac OS 10.11——虚拟机安装黑苹果

    VirtualBox安装MacOS10.11,安装日期:2016/5/14用虚拟机装黑苹果本人也装了不下3次了,这次为了做这个教程还特意把virtualbox和旧版的MacOS删了,重新再装一遍。所以保证能运行,不像网上其他教程都是导出复制,还不要脸的贴个原创。VirtualBox是官网下的最新版:5.0.20forWindowshostsx

  • sop流程图模板_这是一份标准作业流程SOP详解,附流程图绘制规范,不愁不会画!…「建议收藏」

    sop流程图模板_这是一份标准作业流程SOP详解,附流程图绘制规范,不愁不会画!…「建议收藏」注:资料来源百度、档即用网,品质人生质量开讲平台搜集、整理、编辑,仅供学习交流所用,请勿做其他用途!小编辛苦整理,转载请注明出处。什么是SOP?StandardOperationProcedure所谓SOP,是StandardOperationProcedure三个单词中首字母的大写,即标准作业程序。是以文件的形式描述作业员在生产作业过程中的操作步骤和应遵守的事项;是作业员的作业指导书…

  • linux tcp发包工具_怎么用命令行查IP

    linux tcp发包工具_怎么用命令行查IPSendip是一个linux平台的命令行发数据包工具,目前(2018年2月)支持的协议有ipv4、ipv6、icmp、tcp、udp、bgp、rip、ntp,作者表示其他协议将会后面支持,当他有空写的时候。Sendip很强大,它支持自定义头部和数据(也就是IP层以上的整个包),没有过多的限制,所以连源IP都可以随意写,而且里面也提供了一些默认的选项,可以择需而发,非常方便。又因为它是命令行的,还支…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号