普通平衡树

模板题链接

1、引入

一种二叉树,这棵树满足任意一个节点,它的左儿子的权值<自己的权值<右儿子的权值
这种树叫做二叉查找树,这个概念应该在初赛中见过了吧

Splay就是利用这个结构来实现的

2、变量

模板题的7大变量

  • sz:表示当前节点个数
  • rt:表示当前根节点的编号
  • f[x]:表示编号x节点的父亲的编号
  • key[x]:表示编号为x的节点的对应的数值
  • size[x]:表示以编号为x的节点为根的子树的节点个数
  • recy[x]:表示以编号为x的节点的数值重复次数
  • son[x][0]/son[x][1]:表示以编号为x的节点的左/右儿子的编号

3、操作

清空一个节点
inline void clear(int x){f[x] = key[x] = size[x] = recy[x] = son[x][0] = son[x][1] = 0;//5个数组全部清零
}
确定一个节点是父亲的左儿子还是右儿子
int get(int x){return son[f[x]][1] == x;//如果自己是父亲的右儿子,返回1;否则返回0(0为左儿子,1为右儿子)
}
更新一个节点
inline void update(int x){if (x){//如果这个点存在size[x] = recy[x];//自己重复的次数先累计if (son[x][0]) size[x] += size[son[x][0]];if (son[x][1]) size[x] += size[son[x][1]];//如果存在儿子,把儿子的size累积到自己//然后发现一个问题,更新自己的size时,必须保证儿子的size是正确的//所以后面的操作,当牵扯到儿子和父亲时,应该先更新新儿子,后更新新父亲}
}
把一个点连到另一点下面
void connect(int x, int y, int z){//x连到y下面,关系为z if (x) f[x] = y;//存在x,则x的父亲为y if (y) son[y][z] = x;//存在y,y的z关系儿子为x
}
上旋

看图
情况1
发现:绿点上旋,绿点是父亲橙点的左儿子,所以绿点的右儿子紫点变成橙点的左儿子,橙点变成绿点的右儿子,绿点变成红点的左儿子

情况2
发现:绿点上旋,绿点是父亲黄点的右儿子,所以绿点的左儿子蓝点变成黄点的右儿子,黄点变成绿点的左儿子,绿点变成红点的右儿子


上面两种情况是有一种共同特点的
Code:

void rotate(int x){//上旋x int fa = f[x], ffa = f[fa], m = get(x), n = get(fa);//确定x,fa的关系 connect(son[x][m ^ 1], fa, m);//把要转的儿子(关系为m^1的儿子)转到父亲下,关系为m connect(fa, x, m ^ 1);//把父亲转到自己下面,关系为m^1 connect(x, ffa, n);//把自己转到父亲的父亲下,关系为n update(fa), update(x);//先更新fa,再更新自己,可以自己想想为什么是这个顺序
}
splay

把一个点一直上旋,旋到规定点,此题全部是旋到根节点,所以默认旋到根节点
设当前splay的点为x
如果x的父亲是根,直接旋;
否则分两种情况:

  • get(x)=get(fa),此时,先上旋fa,再上旋x
  • get(x)!=get(fa),此时连续上旋两次x

为什么分两种情况?可以自行画图看一看
发现按照上述旋转,每次splay以后,整棵树十分的平衡!
(接近于完全二叉树)
如果不分情况,直接无脑上旋,则会结构变得比较乱

splay操作是算法的核心,它保证的二叉树的随机性,平衡性
所以,当你在打splay的时候,记住一条准则:有事没事splay一下

void splay(int x){for (int fa; fa = f[x]; rotate(x))//每次总是旋转自己 if (f[fa]) rotate(get(x) == get(fa) ? fa : x);//如果有爷爷(父亲的父亲),看父亲与父亲的父亲的关系决定转哪个 rt = x;//别忘了,把根赋为当前点
}
插入一个点
void insert(int x){if (!rt){//树中没有一个节点 rt = ++sz;key[rt] = x;size[rt] = recy[rt] = 1;son[rt][0] = son[rt][1] = 0;//赋初值 return;}int now = rt, fa = 0;while (1){if (key[now] == x){//树中已有此点,重复+1 ++recy[now];update(now); update(fa);splay(now);//splay一下,保证平衡 return;}fa = now, now = son[now][x > key[now]];//满足二叉查找树的性质,往下跑 if (!now){++sz;key[sz] = x;size[sz] = recy[sz] = 1;//赋初值f[sz] = fa;//父亲是fa son[fa][x > key[fa]] = sz;//更新父亲的新儿子 update(fa);//更新父亲的size splay(sz);//splay一下,保证平衡return;}}
}
查询一个数的排名
int find(int x){//查排名 int now = rt, ans = 0;while (1){if (x < key[now]){now = son[now][0]; continue;//在左子树中 }ans += size[son[now][0]];//排名加上左子树节点个数 if (x == key[now]){ splay(now); return ans + 1; }//值等于当前点,splay一下,保证平衡,排名+1为当前排名 ans += recy[now];//排名加上当前节点的数的个数 now = son[now][1];//在右子树中 }
}
查询排名为k的数
int kth(int x){//查找排名为x的数 int now = rt;while (1){if (son[now][0] && x <= size[son[now][0]]){//在左子树中 now = son[now][0]; continue;}if (son[now][0]) x -= size[son[now][0]];//存在左儿子,排名减去左子树节点数 if (x <= recy[now]){ splay(now); return key[now]; }//说明就是当前点,splay一下,保证平衡,退出 x -= recy[now];//排名减去当前节点数的个数 now = son[now][1];//在右子树中 }
}
前驱、后继

采用全新的方法
首先,插入目标新节点,使该节点在根上
那么它的前驱为左子树中最大的那个
后继为右子树中最小的那个
最后,当然要删掉刚才插入的节点

至于为什么,想想就知道了

if (opt == 5){insert(x); printf("%d\n", key[pre()]); del(x);//insert->del}if (opt == 6){insert(x); printf("%d\n", key[nxt()]); del(x);//insert->del}
int pre(){//前驱为左子树中最大的那个 int now = son[rt][0];while (son[now][1]) now = son[now][1];return now;
}int nxt(){//后继为右子树中最小的那个 int now = son[rt][1];while (son[now][0]) now = son[now][0];return now;
}
删除一个点
void del(int x){int no_use = find(x);//find主要是把当前数的对应点找到,然后旋到根,返回值的排名在这里没用 if (recy[rt] > 1){//情况1:有重复,重复-1,更新,退出 --recy[rt];update(rt);return;}//接下来都是没有重复的情况 if (!son[rt][0] && !son[rt][1]){//情况2:没有儿子,直接清空 clear(rt);rt = 0;return;}if (!son[rt][0]){//情况3:没有左儿子,只有右儿子,右儿子变成根,清除自己 int tmp = rt;f[rt = son[rt][1]] = 0;clear(tmp);return;}if (!son[rt][1]){//情况4:没有右儿子,只有左儿子,左儿子变成根,清除自己 int tmp = rt;f[rt = son[rt][0]] = 0;clear(tmp);return;}//情况5:两个儿子都有,这是需要一个很简便的做法//把前驱splay到根,保持左子树其他节点不用动//原根右儿子变成前驱的右儿子//原根功成身退,清除掉//最后对前驱的size进行更新 int tmp = rt, left = pre();splay(left);connect(son[tmp][1], rt, 1);clear(tmp);update(rt);
}

4、模板题

全在上面了,直接Code:

#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
int sz, rt, f[maxn], key[maxn], size[maxn], recy[maxn], son[maxn][2];inline int read(){int s = 0, w = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);return s * w;
}void clear(int x){f[x] = key[x] = size[x] = recy[x] = son[x][0] = son[x][1] = 0;//5个数组全部清零
}int get(int x){return son[f[x]][1] == x;//如果自己是父亲的右儿子,返回1;否则返回0(0为左儿子,1为右儿子)
}void update(int x){if (x){//如果这个点存在size[x] = recy[x];//自己重复的次数先累计if (son[x][0]) size[x] += size[son[x][0]];if (son[x][1]) size[x] += size[son[x][1]];//如果存在儿子,把儿子的size累积到自己//然后发现一个问题,更新自己的size时,必须保证儿子的size是正确的//所以后面的操作,当牵扯到儿子和父亲时,应该先更新新儿子,后更新新父亲}
}void connect(int x, int y, int z){//x连到y下面,关系为z if (x) f[x] = y;//存在x,则x的父亲为y if (y) son[y][z] = x;//存在y,y的z关系儿子为x
}void rotate(int x){//上旋x int fa = f[x], ffa = f[fa], m = get(x), n = get(fa);//确定x,fa的关系 connect(son[x][m ^ 1], fa, m);//把要转的儿子转到父亲下,关系为m connect(fa, x, m ^ 1);//把父亲转到自己下面,关系为m^1 connect(x, ffa, n);//把自己转到父亲的父亲下,关系为n update(fa), update(x);//先更新fa,再更新自己,可以自己想想为什么是这个顺序
}void splay(int x){for (int fa; fa = f[x]; rotate(x))//每次总是旋转自己 if (f[fa]) rotate(get(x) == get(fa) ? fa : x);//如果有爷爷(父亲的父亲),看父亲与父亲的父亲的关系决定转哪个 rt = x;//别忘了,把根赋为当前点
}void insert(int x){if (!rt){//树中没有一个节点 rt = ++sz;key[rt] = x;size[rt] = recy[rt] = 1;son[rt][0] = son[rt][1] = 0;//赋初值 return;}int now = rt, fa = 0;while (1){if (key[now] == x){//树中已有此点,重复+1 ++recy[now];update(now); update(fa);splay(now);//splay一下,保证平衡 return;}fa = now, now = son[now][x > key[now]];//满足二叉查找树的性质,往下跑 if (!now){++sz;key[sz] = x;size[sz] = recy[sz] = 1;//赋初值f[sz] = fa;//父亲是fa son[fa][x > key[fa]] = sz;//更新父亲的新儿子 update(fa);//更新父亲的size splay(sz);//splay一下,保证平衡return;}}
}int find(int x){//查排名 int now = rt, ans = 0;while (1){if (x < key[now]){now = son[now][0]; continue;//在左子树中 }ans += size[son[now][0]];//排名加上左子树节点个数 if (x == key[now]){ splay(now); return ans + 1; }//值等于当前点,splay一下,保证平衡,排名+1为当前排名 ans += recy[now];//排名加上当前节点的数的个数 now = son[now][1];//在右子树中 }
}int kth(int x){//查找排名为x的数 int now = rt;while (1){if (son[now][0] && x <= size[son[now][0]]){//在左子树中 now = son[now][0]; continue;}if (son[now][0]) x -= size[son[now][0]];//存在左儿子,排名减去左子树节点数 if (x <= recy[now]){ splay(now); return key[now]; }//说明就是当前点,splay一下,保证平衡,退出 x -= recy[now];//排名减去当前节点数的个数 now = son[now][1];//在右子树中 }
}int pre(){//前驱为左子树中最大的那个 int now = son[rt][0];while (son[now][1]) now = son[now][1];return now;
}int nxt(){//后继为右子树中最小的那个 int now = son[rt][1];while (son[now][0]) now = son[now][0];return now;
}void del(int x){int no_use = find(x);//find主要是把当前数的对应点找到,然后旋到根,返回值的排名在这里没用 if (recy[rt] > 1){//情况1:有重复,重复-1,更新,退出 --recy[rt];update(rt);return;}//接下来都是没有重复的情况 if (!son[rt][0] && !son[rt][1]){//情况2:没有儿子,直接清空 clear(rt);rt = 0;return;}if (!son[rt][0]){//情况3:没有左儿子,只有右儿子,右儿子变成根,清除自己 int tmp = rt;f[rt = son[rt][1]] = 0;clear(tmp);return;}if (!son[rt][1]){//情况4:没有右儿子,只有左儿子,左儿子变成根,清除自己 int tmp = rt;f[rt = son[rt][0]] = 0;clear(tmp);return;}//情况5:两个儿子都有,这是需要一个很简便的做法//把前驱splay到根,保持左子树其他节点不用动//原根右儿子变成前驱的右儿子//原根功成身退,清除掉//最后对前驱的size进行更新 int tmp = rt, left = pre();splay(left);connect(son[tmp][1], rt, 1);clear(tmp);update(rt);
}int main(){int M = read();while (M--){int opt = read(), x = read();if (opt == 1) insert(x);if (opt == 2) del(x);if (opt == 3) printf("%d\n", find(x));if (opt == 4) printf("%d\n", kth(x));if (opt == 5){insert(x); printf("%d\n", key[pre()]); del(x);}if (opt == 6){insert(x); printf("%d\n", key[nxt()]); del(x);}}return 0;
}

5、小建议

建议完全理解splay的代码
多看看

不知道代码哪错了建议重构代码
建议过些天复习复习

文艺平衡树

模板题链接

1、引入

说白了,区间翻转

2、原理

发现一棵二叉查找树,中序遍历是有序的
既然这样,何不先按顺序加入二叉树?

对于每个操作 [ l , r ] [l,r] [l,r],找到 k t h ( l − 1 ) , k t h ( r + 1 ) kth(l-1),kth(r+1) kth(l−1),kth(r+1)
分别记为 p r e , n x t pre, nxt pre,nxt
把 p r e pre pre旋到根,把 n x t nxt nxt旋到根的(右)儿子
发现: n x t nxt nxt的左子树节点集合= [ l , r ] [l,r] [l,r]节点集合
因为对于任何一个 x ∈ [ l , r ] x∈[l,r] x∈[l,r],都有 v p r e < v x < v n x t v_{pre}<v_{x}<v_{nxt} vpre​<vx​<vnxt​
必定都在nxt左子树中,且nxt左子树没有别的节点

说明整个子树翻转一下就行啦

怎么做呢?采用打标记的方法
在nxt左儿子上打一个tag, t a g = 1 tag=1 tag=1说明这个区间需要翻转

下传标记,在每次求kth,splay的时候pushdown一下,反正能pushdown就pushdown,多了不会慢太多,少了万一wa了呢~~,其实自己分析一下也可以知道哪些需要pushdown,不过都下传保险嘛,万一分析错了呢

如何pushdown?首先儿子的tag^=1,至于为什么是亦或自己想想就行
然后交换两个儿子的编号(因为翻转),最后自己的tag=0

3、代码实现

首先,需要注意,插入-inf与inf,防止pre和nxt找不到

加入节点
    for (int i = 1; i <= n + 2; ++i) insert(i);//1~n全体+1,1为-inf,n+2为inf
void insert(int x){int now = rt, fa = 0;while(now) fa = now, now = son[now][x > key[now]];//插入新节点,知道可以插入为止now = ++sz;key[now] = x;f[now] = fa;if (fa) son[fa][x > key[fa]] = now;size[now] = 1;son[now][0] = son[now][1] = 0;splay(now, 0);//旋到根,保持平衡
}
上旋
void connect(int x, int y, int z){ f[x] = y, son[y][z] = x; }
void rotate(int x){int fa = f[x], ffa = f[fa], m = get(x), n = get(fa);connect(x, ffa, n);connect(son[x][m ^ 1], fa, m);connect(fa, x, m ^ 1);update(fa); update(x);
}
//注意这里的splay跟上面普通splay不一样
void splay(int x, int goal){int len = 0;for (int i = x; i; i = f[i]) q[++len] = i;for (int i = len; i; --i) pushdown(q[i]);//先把可能经过的节点全部下传一遍,注意下传顺序while (f[x] != goal){//没旋到goalint fa = f[x];if (f[fa] != goal) rotate(get(x) == get(fa) ? fa : x);rotate(x);}if (!goal) rt = x;//是不是旋到根节点
}
kth
int kth(int x){ int now = rt;while (1){ pushdown(now);//下传标记if (size[son[now][0]] >= x) now = son[now][0]; else{x -= size[son[now][0]];if (x == 1) return now;--x, now = son[now][1]; }}
}
翻转

翻转区间 [ l , r ] [l,r] [l,r]

void work(int l, int r){l = kth(l); r = kth(r + 2);//找到pre,nxtsplay(l, 0); splay(r, l);//上旋tag[son[r][0]] ^= 1;//打标记,注意这里也是亦或
}
下传标记
void pushdown(int x){if (tag[x]){//如果有标记tag[x] = 0;//自己变为0tag[son[x][0]] ^= 1;tag[son[x][1]] ^= 1;//下传swap(son[x][0], son[x][1]);//翻转儿子}
}
输出
void write(int x){pushdown(x);//下传if (son[x][0]) write(son[x][0]);//中序遍历 左中右if (key[x] > 1 && key[x] < n + 2) printf("%d ", key[x] - 1);//注意一开始都+1,最后-1if (son[x][1]) write(son[x][1]);
}

4、模板题

Code:

#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
int f[maxn], key[maxn], son[maxn][2], size[maxn], q[maxn], tag[maxn], n, m, rt, sz;inline int read(){int s = 0, w = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);return s * w;
}void update(int x){size[x] = 1; if (son[x][0]) size[x] += size[son[x][0]];if (son[x][1]) size[x] += size[son[x][1]];
}int get(int x){ return son[f[x]][1] == x; }void pushdown(int x){if (tag[x]){tag[x] = 0;tag[son[x][0]] ^= 1;tag[son[x][1]] ^= 1;swap(son[x][0], son[x][1]);}
}void connect(int x, int y, int z){ f[x] = y, son[y][z] = x; }void rotate(int x){int fa = f[x], ffa = f[fa], m = get(x), n = get(fa);connect(x, ffa, n);connect(son[x][m ^ 1], fa, m);connect(fa, x, m ^ 1);update(fa); update(x);
}void splay(int x, int goal){int len = 0;for (int i = x; i; i = f[i]) q[++len] = i;for (int i = len; i; --i) pushdown(q[i]);while (f[x] != goal){int fa = f[x];if (f[fa] != goal) rotate(get(x) == get(fa) ? fa : x);rotate(x);}if (!goal) rt = x;
}void insert(int x){int now = rt, fa = 0;while(now) fa = now, now = son[now][x > key[now]];now = ++sz;key[now] = x;f[now] = fa;if (fa) son[fa][x > key[fa]] = now;size[now] = 1;son[now][0] = son[now][1] = 0;splay(now, 0);
}int kth(int x){ int now = rt;while (1){ pushdown(now);if (size[son[now][0]] >= x) now = son[now][0]; else{x -= size[son[now][0]];if (x == 1) return now;--x, now = son[now][1]; }}
}void work(int l, int r){l = kth(l); r = kth(r + 2);splay(l, 0); splay(r, l);tag[son[r][0]] ^= 1;
}void write(int x){pushdown(x);if (son[x][0]) write(son[x][0]);if (key[x] > 1 && key[x] < n + 2) printf("%d ", key[x] - 1);if (son[x][1]) write(son[x][1]);
}int main(){n = read(), m = read();for (int i = 1; i <= n + 2; ++i) insert(i);while (m--){int l = read(), r = read();work(l, r);}write(rt);return 0;
}

5、结语

排版有些乱(⊙﹏⊙汗
我还在寻找自己的风格,每天文化课有些紧,上机时间也不多,题目也做的不多
不是特别熟练也请谅解

习题

[CQOI2014]排序机械臂:练手好题,加深理解
[NOI2004]郁闷的出纳员:有点思维难度
送花:裸题,你需要很快的秒了
宝石管理系统:如果短时间内完成,说明你掌握的还不错
[HNOI2004]宠物收养场:比较简单的Splay
[ZJOI2006]书架:有点烦,有点难调
[NOI2003]文本编辑器:非常基本的操作训练
[HNOI2012]永无乡:splay dsu
[NOIp2017]列队:较毒瘤,好题
序列终结者:裸题,秒杀
[HNOI2002]营业额统计:裸题++

【学习笔记】Splay相关推荐

  1. 学习笔记——Splay

    前言 前几天有幸听学长讲平衡树,想着好久没写博客了,记录一下. 简介 Splay,平衡树的一种,依靠每次将访问到的点旋到根来保持树的平衡. 并且,Splay 还可以高效解决序列翻转等操作. 实现 前提 ...

  2. 平衡树(splay)学习笔记(详细,从入门到精(bao)通(ling))(持续更新)

    前言 在前几天军训站军姿的时候胡思乱想,突然明白了splay的本质 KMP学习笔记后又一篇字数上万的题解- 前置技能--二叉搜索树 首先来看一个最简单的问题: 你需要维护一个数据结构,资磁这些操作: ...

  3. 平衡树 - FHQ 学习笔记

    平衡树 - FHQ 学习笔记 主要参考万万没想到 的 FHQ-Treap学习笔记. 本片文章的姊妹篇:平衡树 - Splay 学习笔记. 感觉完全不会平衡树,又重新学习了一遍 FHQ,一口气把常见套路 ...

  4. Link Cut Tree 学习笔记

    Link Cut Tree 学习笔记 说在前边 最近补 CF 碰见一道 LCT ,就打算学习一下这个东西...顺便复习一下 splay. 具体算法及实现 参考了FlashHu, Candy? P369 ...

  5. 【学习笔记】线段树详解(全)

    [学习笔记]线段树详解(全) 和三个同学一起搞了接近两个月的线段树,头都要炸了T_T,趁心态尚未凉之前赶快把东西记下来... [目录] [基础]作者:\((Silent\)_\(EAG)\) [懒标记 ...

  6. pbds库学习笔记(优先队列、平衡树、哈希表)

    目录 pbds库学习笔记(优先队列.平衡树.哈希表) 前言 概述 priority_queue优先队列 概述 参数 堆的基本操作的函数 对比STL新增函数 modify修改 Dijkstra最短路径演 ...

  7. PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 call

    您的位置 首页 PyTorch 学习笔记系列 PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 发布: 2017年8月4日 7,195阅读 ...

  8. 容器云原生DevOps学习笔记——第三期:从零搭建CI/CD系统标准化交付流程

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  9. 容器云原生DevOps学习笔记——第二期:如何快速高质量的应用容器化迁移

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  10. 2020年Yann Lecun深度学习笔记(下)

    2020年Yann Lecun深度学习笔记(下)

最新文章

  1. C++11中头文件type_traits介绍
  2. 泛型(派生子类,泛型通配符,类型擦除)
  3. 简易嵌入式管理平台 C 实现
  4. 云计算安全威胁集中营
  5. js实现图片从左到右循环播放
  6. Ubuntu 16.04卸载CUDA 6.5和安装CUDA 8.0
  7. 全国计算机等级考试收费不一样,2018年北京全国计算机等级考试收费标准
  8. mfc之解决vs2010调试监视器(MSVSMON.EXE)未能启动的问题
  9. glBindTexture--纹理
  10. 华三 h3c vrrp和监视端口配置
  11. 计算机软件树状图,树状图怎么画|画树状图步骤
  12. android9 apk自动安装功能,Android app自动更新总结(已适配9.0)
  13. 投影仪与电视的C位之争,电视会成为下一个被淘汰的电器吗?
  14. 重磅!图森王乃岩团队最新工作—TridentNet:处理目标检测中尺度变化新思路
  15. vivo X Note暗藏黑科技,三麦降噪让开黑更安心
  16. 二见钟情——设计模式
  17. 相似对角化的意义(转载)
  18. 2021年G3锅炉水处理考试题及G3锅炉水处理考试题库
  19. http隐蔽隧道搭建
  20. 12.8-静态页面搭建总结

热门文章

  1. ENVI:如何进行遥感图像的分类?(决策树模型)
  2. python获取网页内容 不打开_网页抓取python不返回任何内容
  3. Smalltalk for Everyone Else
  4. Java发邮件(详解+源代码)
  5. 我的软件销售生涯(一)
  6. 信息系统项目管理10大管理
  7. iNavFlight之MSP v2 Sensor报文格式
  8. Filament 渲染引擎剖析 之 多线程渲染 2
  9. matlab fscanf
  10. TensorFlow 使用 slim 模块搭建复杂网络