文章目录

  • 进入正题
    • 珂朵莉树的起源
      • 题目简述
      • 题目分析
      • 珂朵莉树的实现
        • 萌新三连
          • 1.明明查询的右端点是12,但是要split(13)呢?
          • 2.为什么要先分裂右端点,然后再分裂左端点呢?
          • 3.获取到区间之后呢?
        • 其余操作实现
      • 例题总代码
      • 小结
  • 其他例题
    • [SCOI2010]序列操作
    • CF817F MEX Queries
    • 完结撒花

参考文章 : https://www.cnblogs.com/WAMonster/p/10181214.html

珂朵莉是世界上最幸福的女孩







进入正题

珂朵莉树的起源

珂朵莉树原名老司机树(Old Driver Tree,ODT),由2017年一场CF比赛中提出的数据结构,因为题目背景主角是《末日时在做什么?有没有空?可以来拯救吗?》的主角珂朵莉,因此该数据结构被称为珂朵莉树

题目简述

(原题请转->CF896C Willem, Chtholly and Seniorious)

请你写一种奇怪的数据结构,支持:

  • 1 l r x :将 [l,r] 区间所有数加上 x
  • 2 l r x :将 [l,r] 区间所有数改成 x
  • 3 l r x :输出将 [l,r] 区间从小到大排序后的第 x 个数是多少(即区间第 x 小,数字大小相同算多次,保证 1≤x≤r−l+11\leq x \leq r-l+11≤x≤r−l+1 )
  • 4 l r x y :输出[l,r] 区间每个数字的 x次方的和 模 y 的值 (即(∑i=lraix)mody)(\sum^r_{i=l}a_i^x) \mod y )(∑i=lr​aix​)mody)

    【输入格式】 这道题目的输入格式比较特殊,需要选手通过seed 自己生成输入数据。 输入一行四个整数 n,m,seed,vmaxn,m,seed,v_{max}n,m,seed,vmax​(1≤n,m≤105,0≤seed≤109+7,1≤vmax≤109)(1\leq n,m\leq 10^{5},0\leq seed \leq 10^{9}+7, 1\leq v_{max} \leq 10^{9})(1≤n,m≤105,0≤seed≤109+7,1≤vmax​≤109) 其中 n 表示数列长度,m 表示操作次数,后面两个用于生成输入数据。 数据生成的伪代码如下:

def rnd():   ret = seedseed = (seed * 7 + 13) mod 1000000007return retfor i = 1 to n:a[i] = (rnd() mod vmax) + 1for i = 1 to m:op = (rnd() mod 4) + 1l = (rnd() mod n) + 1r = (rnd() mod n) + 1if (l > r): swap(l, r)if (op == 3):x = (rnd() mod (r - l + 1)) + 1else:x = (rnd() mod vmax) + 1if (op == 4):y = (rnd() mod vmax) + 1

其中上面的op指题面中提到的四个操作。

【输出格式】 对于每个操作3和4,输出一行仅一个数。

输入样例 #1:

10 10 7 9

输出 #1:

2
1
0
3

输入样例 #2:

10 10 9 9

输出 #2:

1
1
3
3

题目分析

一说起区间维护问题,我们就能想到线段树,主席树,树状数组,Splay,分块,莫队等数据结构,但是读完题目我们发现,第四个操作涉及每个数字的相关操作,上面提到的结构只有莫队可以做到,但是复杂度太高,我们需要一个更加高效的数据结构 珂朵莉来维护这些操作。

珂朵莉树的实现

联想题目,发现有一个操作为令一片区间值相同,我们自然想到,如果能用一个点来表示这一个区间,那么自然会省很多力,诶,一个点怎么表示一个区间呢 ,于是,自然而然的想到了结构体!

珂朵莉树基于std::set,现在我们先定义一段连续的值相同的区间:

using ll = long long;
struct node{int l,r;//l:区间左端点,r:区间右端点mutable ll val;//区间值bool operator<(const node &a)const {return l<a.l;}//重载<比较符,使区间在set中为从小到大的顺序排列node(int L,int R,ll Val):l(L),r(R),val(Val){}//构造函数(赋值专用)node(int L):l(L){}//构造函数(提取区间专用)
};

mutable 关键字定义一个强制可变量,这样可以使得我们在 set 中修改 val 的值!
由于珂朵莉树基于 std::set 因此接下来自然就是将 node 结点放入set中:

set<node> s;
using  si = set<node>::iterator;//因为set<node>::iterator实在是太长了,为了后续操作的方便,我们给它起个别名 si ,是不是感觉瞬间清爽

然而每次在查询操作时,我们不可能保证查询的区间端点正好在一个结构体结点中,因此需要查找到区间左右端点,获取到待查询或者待修改的区间,这便引出了下面的分裂函数:

si split(int pos){si it = s.lower_bound(node(pos));//获取第一个左端点不小于pos的结点的迭代器if(it != s.end() && it->l==pos) return it;//如果左端点已经是pos并且不是末尾,直接返回该迭代器--it;//否则pos一定在前一个结点区间内int l=it->l,r=it->r;//保存结点信息ll val = it->val;s.erase(it);//删除该结点s.insert(node(l,pos-1,val));//插入[l,pos-1]这个区间return s.insert(node(pos,r,val)).first;//插入[pos,r]区间,并返回指向该区间的迭代器
}

split函数的作用就是查找set中第一个左端点不小于pos的结点,如果找到的结点的左端点等于pos便直接返回指向该结点的迭代器,如果不是,说明pos包含在前一个结点所表示的区间之间,此时便直接删除包含pos的结点,然后以pos为分界点,将此结点分裂成两份,分别插入set中,并返回指向后一个分裂结点的迭代器。

有点抽象?下面以图示说明split函数到底做了什么:

  • 首先我们假设 set 中有三个node结点,这三个结点所表示的区间长度为14 ,如下图:
  • 不妨以提取区间 [10,12] 为例详细展开((躁动的读者)诶,说好的查询10到12呢,怎么下面扯了一堆13?别急,后续将会揭晓 ):
  • 如果我们要查询序列第13个位置,首先执行si it = s.lower_bound(node(13)); 此时it将成为一个指向第三个结点的迭代器,为什么是第三个结点,而不是第二个结点呢,因为lower_bound这个函数获取的是第一个左端点l不小于13的结点,所以it是指向第三个结点的。

    然后执行判断语句,发现第三个结点的左端点不是13,不满足条件,说明13必包含在前一个结点中,继续向下执行,让it指向前一个结点:

    先将该结点的信息保存下来:int l = 10, r = 13, val = 2
    然后直接删除该结点

    pos为分界点,将被删除的结点分裂为 [l,pos-1] ,[pos,r]这两块,并返回指向[pos,r]这个区间的迭代器,事实上return s.insert(node(pos,r,val)).first; ,便做到了插入[pos,r]这端区间,并返回指向它的迭代器,有一个insert函数返回值为pair类型,其中pair的第一个元素就是元素插入位置的迭代器。

    至此13位置已经分裂完成,然后是查询第10个位置,查询步骤同上,但是10号点满足if语句,便直接返回了。
  • 由上述步骤,为了提取区间 [10,12],我们执行了两次 split ,一次为split(13),一次为split(10),并获得了两个迭代器,一个指向第二结点,一个指向第三结点。

萌新三连

1.明明查询的右端点是12,但是要split(13)呢?
  • 是时候瞎扯了 : 由于本人水平有限,以下全为瞎扯 我们联想迭代器,以大家最熟悉的std::vector来说,它有两个正向迭代器begin()和end() ,大家都知道begin()是指向首个元素的迭代器,而end()呢,它并不指向末尾元素,而是指向末尾元素的下一个元素。这样我们的迭代器循环就是这个形式for(vector<int>::iterator it = v.begin();it!=v.end();++it) , 因此我们可以直接写出当获取到这两个迭代器后如何遍历区间for(si it = itl;it != itr; ++it) 不过直接split(12),再插入[l,pos]和[pos+1,r],循环改成for(si it = itl;it <= itr; ++it),貌似也没错?但我看别人都是split(r+1),或许是为了和STL保持形式一致,便于直接使用STL的内置函数吧,毕竟STL的函数都是包首不包尾的
2.为什么要先分裂右端点,然后再分裂左端点呢?
  • 因为如果先分裂左端点,返回的迭代器会位于所对应的区间以 l 为左端点,此时如果r也在这个节点内,就会导致分裂左端点返回的迭代器被 erase 掉,导致 RE
  • 结合问题1和问题2 ,获取区间迭代器时,务必写成如下格式 si itr = split(r+1), itl = split(l); 起名无所谓,按自己的习惯就好
3.获取到区间之后呢?
  • 问的好 获取区间后就可以直接根据操作暴力进行了!
  • 可是这样不会退化为暴力吗?
  • 额 这就涉及到珂朵莉树的不可或缺的操作了-----将一个区间全部改为某个值。
void assign(int l,int r,ll val){si itr=split(r+1),itl=split(l);//务必先获取r+1,然后再获取ls.erase(itl,itr);//直接删除这两个迭代器之间的所有结点s.insert(node(l,r,val));//插入一个新值的区间
}

回想前文珂朵莉树node结构体的引出,我们是为了用一个点来表示一个区间,这个区间的值自然只能是相同的。那么这样做真的可以加快速度嘛,确实,不难看出上面的assign可以将多个结点变为一个!

由于题目的数据随机,因此assign操作大约占到所有操作的0.25,下面我们以一个随机程序,模拟set中结点的数目:

#include<bits/stdc++.h>
using namespace std;struct node{int l,r;mutable ll val;bool operator<(const node &a)const {return l<a.l;}node(int L,int R,ll Val):l(L),r(R),val(Val){}node(int L):l(L){}
};set<node> s;
using  si = set<node>::iterator;si split(int pos){si it = s.lower_bound(node(pos));if(it != s.end() && it->l==pos) return it;--it;int l=it->l,r=it->r;ll val = it->val;s.erase(it);s.insert(node(l,pos-1,val));return s.insert(node(pos,r,val)).first;
}void assign(int l,int r,ll val){si itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert(node(l,r,val));
}int main() {int n;scanf("%d", &n);for (int i = 1; i <= n + 1; ++i)s.insert(node(i, i, 0));srand((unsigned)time(0));srand(rand());for (int t = 1; t <= n; ++t) {int a = rand() * rand() % n + 1, b = rand() * rand() % n + 1;if (a > b) swap(a, b);if (rand() % 4 == 0) {assign(a, b, 0);}else split(b + 1), split(a);}printf("%d", s.size());return 0;
}

本人电脑实验如下:(n表示序列长度,f(n)表示set中结点个数)

n f(n)
10 8
100 21
1000 36
10000 55
100000 54
1000000 64

可见加了assign的珂朵莉跑的超级快!结点数达到了log级。因此珂朵莉的高效是由随机分配的 assign 来保证的!

其余操作实现

经过上文的 拷打 分析,我们已经不难写出代码了,只需写出 split和assign函数,其余操作就全是暴力了!!

  • 操作一:区间所有数加上 x
//提取区间,暴力加值
void add(int l,int r,ll val){si itr=split(r+1),itl=split(l);for(si it=itl;it!=itr;++it)it->val += val;
}
  • 操作三:求区间第k小数
//提取区间,快速排序,暴力查值
ll kth(int l,int r,int k){si itr=split(r+1),itl=split(l);vector<pair<ll,int>> v;v.clear();for(si it=itl;it!=itr;++it)v.push_back(pair<ll,int>(it->val,it->r-it->l+1));sort(v.begin(),v.end());for(int i=0;i<v.size();++i){k -= v[i].second;if(k<=0) return v[i].first;}return -1;//不存在
}
  • 操作四:求区间所有数的x次方的和模y的值
//快速幂取模
ll qpow(ll a,int b,ll m){ll t = 1ll;a %= m;while(b){if(b&1) t= (t*a)%m;a = (a*a)%m;b>>=1;}return t;
}
//提取区间,暴力运算
ll query(int l,int r,int x,int y){si itr=split(r+1),itl=split(l);ll res(0);for(si it=itl;it!=itr;++it)res=(res+(it->r-it->l+1)*qpow(it->val,x,y))%y;return res;
}

例题总代码

珂朵莉,推平它!

#include<bits/stdc++.h>
using namespace std;
using ll = long long;struct node{int l,r;mutable ll val;bool operator<(const node &a)const {return l<a.l;}node(int L,int R,ll Val):l(L),r(R),val(Val){}node(int L):l(L){}
};set<node> s;
using  si = set<node>::iterator;si split(int pos){si it = s.lower_bound(node(pos));if(it != s.end() && it->l==pos) return it;--it;int l=it->l,r=it->r;ll val = it->val;s.erase(it);s.insert(node(l,pos-1,val));return s.insert(node(pos,r,val)).first;
}void assign(int l,int r,ll val){si itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert(node(l,r,val));
}void add(int l,int r,ll val){si itr=split(r+1),itl=split(l);for(si it=itl;it!=itr;++it)it->val += val;
}ll kth(int l,int r,int k){si itr=split(r+1),itl=split(l);vector<pair<ll,int>> v;v.clear();for(si it=itl;it!=itr;++it)v.push_back(pair<ll,int>(it->val,it->r-it->l+1));sort(v.begin(),v.end());for(int i=0;i<v.size();++i){k -= v[i].second;if(k<=0) return v[i].first;}return -1;
}ll qpow(ll a,int b,ll m){ll t = 1ll;a %= m;while(b){if(b&1) t= (t*a)%m;a = (a*a)%m;b>>=1;}return t;
}ll query(int l,int r,int x,int y){si itr=split(r+1),itl=split(l);ll res(0);for(si it=itl;it!=itr;++it)res=(res+(it->r-it->l+1)*qpow(it->val,x,y))%y;return res;
}int n, m, vmax;
ll seed;
int rnd() {int ret = (int)seed;seed = (seed * 7 + 13) % 1000000007;return ret;
}
int main() {scanf("%d%d%lld%d", &n, &m, &seed, &vmax);for (int i = 1; i <= n; ++i) {int a = rnd() % vmax + 1;s.insert(node(i, i, (ll)a));}s.insert(node(n + 1, n + 1, 0));for (int i = 1; i <= m; ++i) {int l, r, x, y;int op = rnd() % 4 + 1;l = rnd() % n + 1, r = rnd() % n + 1;if (l > r) swap(l, r);if (op == 3) x = rnd() % (r - l + 1) + 1;else x = rnd() % vmax + 1;if (op == 4) y = rnd() % vmax + 1;if (op == 1) add(l, r, (ll)x);else if (op == 2) assign(l, r, (ll)x);else if (op == 3) printf("%lld\n", kth(l, r, x));else if (op == 4) printf("%lld\n", query(l, r, x, (ll)y));}return 0;
}

小结

经过以上层层 拷打 分析,我们已经了解了珂朵莉!而且我们已经知道了 如果一个题目没有区间赋值操作或者有数据点没有赋值操作,或者数据很不随机,请不要把珂朵莉树当正解打

关于珂朵莉树的复杂度可以前往 -> https://zhuanlan.zhihu.com/p/102786071

其他例题

上啊,珂朵莉

[SCOI2010]序列操作

题目请转->https://ac.nowcoder.com/acm/problem/20279
友情提示,不要尝试去用珂朵莉干了洛谷的这道题目,丧心病狂的 管理已经添加了卡珂朵莉的数据点!所以这里以牛客网的题目为例。

  • 题目简述:维护一个01序列,有五种操作:
    0 a b 把区间[a,b]内的所有数全变成0
    1 a b 把区间[a,b]内的所有数全变成1
    2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0
    3 a b 询问[a, b]区间内总共有多少个1
    4 a b 询问[a, b]区间内最多有多少个连续的1

注意到操作0,1全是推平一块区间,珂朵莉狂喜!

  • 参考代码:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;struct node{int l,r;mutable int val;bool operator<(const node &a)const {return l<a.l;}node(int L,int R,int Val):l(L),r(R),val(Val){}node(int L):l(L){}
};set<node> s;
using si = set<node>::iterator;si split(int pos){si it = s.lower_bound(node(pos));if(it != s.end() && it->l==pos) return it;--it;int l=it->l,r=it->r;int val = it->val;s.erase(it);s.insert(node(l,pos-1,val));return s.insert(node(pos,r,val)).first;
}void assign(int l,int r,int val){si itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert(node(l,r,val));
}//区间取反
void negates(int l,int r){si itr=split(r+1),itl=split(l);for(si it=itl;it!=itr;++it)it->val = !it->val;
}
//区间有多少1
int ones(int l,int r){int ans(0);si itr=split(r+1),itl=split(l);for(si it=itl;it!=itr;++it)if(it->val) ans += it->r-it->l+1;return ans;
}
//最长连续的1
int maxseq(int l,int r){int ans(0),cnt(0);si itr=split(r+1),itl=split(l);bool flag(false);for(si it=itl;it!=itr;++it){if(it->val){flag = true;cnt+= it->r-it->l+1;}else if(!it->val && flag){ans = max(ans,cnt);flag = false;cnt = 0;}}ans = max(ans,cnt);//防止区间全是1时,ans在循环里面没来得及更新return ans;
}int n,q;int main() {scanf("%d%d",&n,&q);//题目序列以0下标开始,为了习惯,我们以1开始for(int i=1;i<=n;++i){int a; scanf("%d",&a);s.insert(node(i,i,a));}while(q--){int flag,l,r;scanf("%d %d %d",&flag,&l,&r);//注意本题序列是以0为下标开始的,而我们是以1开始的,所以要手动加1if(flag == 0)      assign(l+1,r+1,0);else if(flag == 1) assign(l+1,r+1,1);else if(flag == 2) negates(l+1,r+1);else if(flag == 3) printf("%d\n",ones(l+1,r+1));else if(flag == 4) printf("%d\n",maxseq(l+1,r+1));}   return 0;
}

CF817F MEX Queries

题目请转->https://www.luogu.com.cn/problem/CF817F

  • 题目简述:维护一个集合,初始为空,有3种操作:
    1 把[l,r] 中在集合中没有出现过的数添加到集合中。
    2 把[l,r] 中在集合中出现过的数从集合中删掉。
    3 把[l,r] 中在集合中没有出现过的数添加到集合中,并把 [l,r] 中在集合中出现过的数从集合中删掉。
    每次操作后输出在集合中没有出现过的最小正整数
    1≤n≤105,1≤l≤r≤10181 \leq n \leq 10^5 , 1 \leq l \leq r \leq 10^{18}1≤n≤105,1≤l≤r≤1018

简单分析一下各种操作,发现操作一其实就是把一块区间赋值为 1 ,操作二就是把一块区间赋值为 0 ,而操作三就是把一块区间内的1变成0,把0变成1。
然后看一下输出,每次输出集合里面没有出现过的最小正整数,其实就是求第一个0的下标,结合珂朵莉树的性质,直接无脑从1开始往后面搜第一个0即可!

  • 参考代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;#define putlen putchar('\n')//快读
inline ll read(){ll X=0; bool flag=1; char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}if(flag) return X;return ~(X-1);
}
//快输
inline void print(ll x){if(x<0){putchar('-');x=-x;}if(x>9) print(x/10);putchar(x%10+'0');
}struct node{ll l,r;mutable int val;bool operator<(const node &a)const {return l<a.l;}node(ll L,ll R,int Val):l(L),r(R),val(Val){}node(ll L):l(L){}
};set<node> s;
using si = set<node>::iterator;inline si split(ll pos){si it = s.lower_bound(node(pos));if(it != s.end() && it->l==pos) return it;--it;ll l=it->l,r=it->r;int val = it->val;s.erase(it);s.insert(node(l,pos-1,val));return s.insert(node(pos,r,val)).first;
}inline void assign(ll l,ll r,int val){si itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert(node(l,r,val));
}
//区间0变1,1变0
inline void rever(ll l,ll r){si itr=split(r+1),itl=split(l);for(si it=itl;it!=itr;++it)it->val=!it->val;
}int n;int main(){n=read();s.insert(node(1,1e19,0));while(n--){ll flag=read(),l=read(),r=read();if(flag == 1)      assign(l,r,1);else if(flag == 2) assign(l,r,0);else if(flag == 3) rever(l,r);//直接从起点开始暴力向后搜for(si it=s.begin();it!=s.end();++it)if(!it->val){print(it->l),putlen;break;}}return 0;
}

完结撒花

泪目

我的算法不可能这么简单—珂朵莉树相关推荐

  1. 算法自学__珂朵莉树

    参考资料: https://zhuanlan.zhihu.com/p/106353082 https://blog.csdn.net/CC_dsm/article/details/98166835 珂 ...

  2. [转]我的数据结构不可能这么可爱!——珂朵莉树(ODT)详解

    参考资料: Chtholly Tree (珂朵莉树) (应某毒瘤要求,删除链接,需要者自行去Bilibili搜索) 毒瘤数据结构之珂朵莉树 在全是珂学家的珂谷,你却不知道珂朵莉树?来跟诗乃一起学习珂朵 ...

  3. 浅谈珂朵莉树(Chtholly Tree)——暴力而玄学的数据结构

    关于它很珂学的名字- 珂朵莉树(Chtholly Tree),又称老司机树(Old Driver Tree),起源于CodeFoeces平台上编号为896C的一道题-- " Willem, ...

  4. 数据结构 【树状数组】【线段树】【珂朵莉树】

    一.区间合并 1.AcWing245你能回答这些问题吗 分析: 线段树.维护四个变量,即可实现区间合并. mx 区间最大连续子段和 mx_l 以区间左端点为左界的最大连续字段和 mx_r 以区间左端点 ...

  5. 【日志】珂学——珂朵莉树

    珂朵莉树 (珂学) 珂朵莉树(或者老司机树)起源于CF896C. 由于之前做到每一组数据都要另外开数据结构,所以现在一些东西就会写为class包装 前置知识点 STL中set的使用(list也行,但是 ...

  6. 一种黑科技:珂朵莉树

    首先要明白的是:珂朵莉树(ODT)是一种用来骗分的暴力数据结构. 珂朵莉树的思想是利用集合set,把相同且连续的元素合并为一个个区间,从而进行区间修改:因此,珂朵莉树是区间的集合,这点可以通过定义结构 ...

  7. 珂朵莉树/ODT 学习笔记

    珂朵莉树/ODT 学习笔记 起源自 CF896C.珂朵莉yyds! 核心思想 把值相同的区间合并成一个结点保存在 set 里面. 用处 骗分.只要是有区间赋值操作的数据结构题都可以用来骗分.在数据随机 ...

  8. 浅谈珂朵莉树(ODT)

    前言 珂学家狂喜( 文章目录 前言 一.珂朵莉树来源 二.珂朵莉树 1.珂朵莉树有什么用? 2.原理是什么? a.存储 b.分割结点 c.推平 d.剩余操作 3.复杂度分析 三.珂朵莉树例题 1.P4 ...

  9. 珂朵莉树(永远喜欢珂朵莉/doge)

    目录 前言 可能用到前置知识 背景 构建珂朵莉树 核心函数 珂朵莉树在实际题目使用 对珂朵莉树的一些感想 最后的最后 前言 最近刚刚学内容大概是借鉴的吧,感觉这个数据结构不仅简单,还很强,有着非常柯学 ...

最新文章

  1. git工作区、暂存区和仓库区
  2. Mysql学习总结(14)——Mysql主从复制配置
  3. 利用javascript和WebGL绘制地球 【翻译】
  4. Java黑皮书课后题第5章:**5.28(显示每月第一天是周几)编写程序,提示用户输入年份和代表概念第一天是周几的数字,然后在控制台显示该年各个月份的第一天是周几
  5. KMP算法的nextval[] 即优化next[]
  6. 5 Vim编辑器的使用
  7. POJ-2400 Supervisor, Supervisee 带权值匹配+枚举所有匹配情况
  8. (回溯 UVa129)困难的串
  9. 【kafka】Kafka 源码解析:Group 协调管理机制
  10. 骁龙870对比天玑1200,到底谁更优秀?
  11. BAT程序员必备技能调研,你中了几招?
  12. Django修改model如何同步数据库
  13. 潭州课堂25班:Ph201805201 tornado 项目 第三课 项目 图片上传,展示 (课堂笔记)...
  14. Response 与 Cookie
  15. 从零开始学WEB前端——HTML理论讲解
  16. g++编译so里调用外部so
  17. linux临时目录不可查询,用find、rm命令清理Linux临时文件夹及检查Linux临时文件夹何时已满...
  18. 2.12 手机GPS定位
  19. Mybatis和MybatisPlus3.4的使用
  20. fcn从头开始_从头开始:简单游戏系列1-抓鱼

热门文章

  1. 什么是 博客 BLOG 什么是 网摘
  2. 高德地图api @amap/amap-jsapi-loader封装成方法(定位、点标记、路径规划、搜索等) 适用于vue等框架
  3. Unity版本捕鱼游戏设计与实现
  4. MII 类型接口介绍
  5. 「Python介绍」Python开发环境
  6. VMware虚拟化之Esxi宿主机内存回收实践
  7. 原生JS表格排序,实现淘宝商品列按价格要求等排序!
  8. 统计信号处理:(估计二) 最小方差无偏估计
  9. drx功能开启后_DRX功能开启指导书(中国移动推荐参数)
  10. 如何做好营销推广方案?| 品牌营销