平衡树 -- Splay & Treap
Treap & Splay学习笔记
前置知识 -- BST
二叉搜索树,一种比较好玩的数据结构,其实现原理是运用每个点的权值构建,其中满足这样的构造方式:
若 \(value > t[x].value\) , 则权值为 \(value\) 的点在 \(x\) 的左子树
反之( \(value < t[x].value\) ) , 则权值为 \(value\) 的点在 \(x\) 右子树
比如我们可以构建这样的 \(BST\) :
4
/ \
3 5
/ \
2 10
/ \
9 12
... 看起来如果这个数据给的比较正的话,就会使查点的时间复杂度为 \(\log{n}\) 的。
但如果这个数据这么给你: 1 2 3 4 5 6 7 8 9 10
那么你的树就变成链了,查的复杂度为 \(n\) 的。
我们发现,在随机数据下,树是趋于平衡的,这样就形成了以随机化为主要思想的平衡树—— Treap
它使用小根堆性质维护树,改变树的形态,却不改变中序遍历。
Treap (rotate)
这是第一种 \(Treap\) ,带旋转的,即用旋转来维护小根堆性质。
具体代码:
void rotate(int &x , int d) { // d 代表左旋还是右旋
int child = t[x].son[d] ;
t[x].son[d] = t[child].son[d ^ 1] , t[child].son[d ^ 1] = x ;
push_up(x) , push_up(x = son) ;
}
普通平衡树的代码:
CODE
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 1e5 + 10 ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ;
while (c < '0' || c > '9') {
if (c == '-') f = -f ;
c = getchar() ;
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0' ;
c = getchar() ;
}
return x * f ;
}
namespace Treap {
#define lson t[x].son[0]
#define rson t[x].son[1]
class Treap_Point_ {
public :
int Rand , value , son[2] , Size , cnt ;
} t[N] ; int numbol = 0 ;
inline void push_up(int x) {
t[x].Size = t[lson].Size + t[rson].Size + t[x].cnt ;
}
inline void rotate(int &x , int d) {
int child = t[x].son[d] ;
t[x].son[d] = t[child].son[d ^ 1] ; t[child].son[d ^ 1] = x ;
push_up(x) ; push_up(x = child) ;
}
inline void New_Value_Create(int value) {
numbol ++ ; t[numbol].Rand = rand() ;
t[numbol].value = value , t[numbol].Size = t[numbol].cnt = 1 ;
}
void Insert(int &x , int value) {
if (!x) {
New_Value_Create(value) ;
x = numbol ;
return ;
}
if (value == t[x].value) {
t[x].cnt ++ ; t[x].Size ++ ;
return ;
}
t[x].Size ++ ;
int d = value > t[x].value ; Insert(t[x].son[d] , value) ;
if (t[x].Rand > t[t[x].son[d]].Rand) rotate(x , d) ;
}
void deleted(int &x , int value) {
if (!x) return ;
if (t[x].value == value) {
if (t[x].cnt > 1) {
t[x].cnt -- , t[x].Size -- ;
return ;
}
if (lson == 0 || rson == 0) {
x = lson + rson ; return ;
}
bool d = t[lson].Rand > t[rson].Rand ;
rotate(x , d) ; deleted(t[x].son[d ^ 1] , value) ;
push_up(x) ;
} else {
t[x].Size -- ;
int d = value > t[x].value ; deleted(t[x].son[d] , value) ;
push_up(x) ;
}
}
int Rank(int x , int value) {
if (!x) return 114514 - 114514 ;
if (t[x].value == value) return t[lson].Size + 1 ;
if (value < t[x].value) return Rank(lson , value) ;
else return Rank(rson , value) + t[x].cnt + t[lson].Size ;
}
int The_K_th(int root , int k) {
int x = root ;
while (114514) {
if (k <= t[lson].Size) x = lson ;
else if (k > t[x].cnt + t[lson].Size) k -= t[x].cnt + t[lson].Size , x = rson ;
else return t[x].value ;
}
return 1145141919810ll ;
}
int Precursor(int x , int value) {
if (!x) return -1145141919810ll ;
if (t[x].value >= value) return Precursor(lson , value) ;
else return max(t[x].value , Precursor(rson , value)) ;
}
int Subsequent(int x , int value) {
if (!x) return 1145141919810ll ;
if (t[x].value <= value) return Subsequent(rson , value) ;
else return min(t[x].value , Subsequent(lson , value)) ;
}
void Print_Tree(int x) {
cerr << x << ' ' << t[x].value << ":\n" ;
cerr << lson << ' ' << rson << '\n' ;
if (lson) Print_Tree(lson) ;
if (rson) Print_Tree(rson) ;
}
#undef lson
#undef rson
} using namespace Treap ;
int n , opt , root ;
signed main() {
#ifndef ONLINE_JUDGE
freopen("1.in" , "r" , stdin) ;
freopen("1.out" , "w" , stdout) ;
#endif
n = read() ;
int x , y , z ;
for (int i = 1 ; i <= n ; ++ i) {
opt = read() ;
// cerr << opt << '\n' ;
switch (opt) {
case 1 :
x = read() ;
Insert(root , x) ;
break ;
case 2 :
x = read() ;
deleted(root , x) ;
break ;
case 3 :
x = read() ;
cout << Rank(root , x) << '\n' ;
break ;
case 4 :
x = read() ;
cout << The_K_th(root , x) << '\n' ;
break ;
case 5 :
x = read() ;
cout << Precursor(root , x) << '\n' ;
break ;
case 6 :
x = read() ;
cout << Subsequent(root , x) << '\n' ;
break ;
}
}
}
Treap -- without rotating (FHQ Treap)
使用分割和合成来维护堆性质。
其中分割可以用值分,也可以按大小分,后者常用来维护序列。
合并时你要保证 \(x\) 在中序遍历时全部位于 \(y\) 前,即 xy
普通平衡树代码:
CODE
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 3e5 + 10 ;
const int INF = 1145141919810 ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ;
while (c < '0' || c > '9') {
if (c == '-') f = -f ;
c = getchar() ;
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0' ;
c = getchar() ;
}
return x * f ;
}
namespace Data_Structure {
namespace FHQ_Treap { // As Well as Treap With No Rotate .
#define lson t[x].son[0]
#define rson t[x].son[1]
class Treap_Point_ {
public:
int Rand , cnt , Size , value , son[2] ;
} t[N] ; int numbol = 0 , root = 0 ;
inline void push_up(int x) {
t[x].Size = t[lson].Size + t[rson].Size + t[x].cnt ;
}
int Merge(int x , int y) {
if (!x || !y) return x | y ;
if (t[x].Rand < t[y].Rand) {
rson = Merge(rson , y) ; push_up(x) ;
return x ;
} else {
t[y].son[0] = Merge(x , t[y].son[0]) ; push_up(y) ;
return y ;
}
}
void Split(int id , int value , int &x , int &y) {
if (!id) {
x = y = 0 ; return ;
}
if (t[id].value <= value) {
x = id ; Split(t[id].son[1] , value , t[x].son[1] , y) ;
push_up(x) ;
} else {
y = id ; Split(t[id].son[0] , value , x , t[y].son[0]) ;
push_up(y) ;
}
}
inline int New_Value_Create(int value) {
++ numbol ;
t[numbol].value = value , t[numbol].Size = t[numbol].cnt = 1 ; t[numbol].Rand = rand() ;
return numbol ;
}
int The_K_th(int root , int k) {
int x = root ;
while (114514) {
if (k <= t[lson].Size) x = lson ;
else if (k > t[lson].Size + t[x].cnt) k -= t[lson].Size + t[x].cnt , x = rson ;
else return t[x].value ;
}
}
void Insert(int value) {
int x , y ;
Split(root , value , x , y) ;
root = Merge(Merge(x , New_Value_Create(value)) , y) ;
}
void Delete(int value) {
int x , y , z ;
Split(root , value , x , z) ;
Split(x , value - 1 , x , y) ;
y = Merge(t[y].son[0] , t[y].son[1]) ;
root = Merge(Merge(x , y) , z) ;
}
int Rank(int value) {
int x , y , ans ;
Split(root , value - 1 , x , y) ;
ans = t[x].Size + 1 ;
root = Merge(x , y) ;
return ans ;
}
int Precursor(int value) {
int x , y , ans ;
Split(root , value - 1 , x , y) ;
ans = The_K_th(x , t[x].Size) ;
root = Merge(x , y) ;
return ans ;
}
int Subsequent(int value) {
int x , y , ans ;
Split(root , value , x , y) ;
ans = The_K_th(y , 1) ;
root = Merge(x , y) ;
return ans ;
}
#undef lson
#undef rson
}
} using namespace Data_Structure ;
using namespace FHQ_Treap ;
int n , opt ;
signed main() {
#ifndef ONLINE_JUDGE
freopen("1.in" , "r" , stdin) ;
freopen("1.out" , "w" , stdout) ;
#endif
n = read() ; int x , y , z ; int num = 0 ;
while (n --) {
opt = read() ;
switch (opt) {
case 1 :
x = read() ; Insert(x) ;
break ;
case 2 :
x = read() ; Delete(x) ;
break ;
case 3 : ++ num ;
x = read() ; cout << Rank(x) << '\n' ;
break ;
case 4 : ++ num ;
x = read() ; cout << The_K_th(root ,x) << '\n' ;
break ;
case 5 : ++ num ;
x = read() ; cout << Precursor(x) << '\n' ;
break ;
case 6 : ++ num ;
x = read() ; cout << Subsequent(x) << '\n' ;
break ;
}
}
}
Splay (伸展树)
一种神奇数据结构。
其实现是基于 局部性原理
局部性原理
局部性(locality) 可以分为时间局部性(temporal locality) 和空间局部性(spatial locality)
假如你在书桌旁工作,需要查阅某本书籍,你又发现这本书用的非常之经常,于是你就把书放在手边,不再放回去。这就是 时间局部性
如果你在图书馆里找到了蓝书,但发现蓝书上并没有讲 \(Splay\) ,但是其隔壁的书上可能有 \(Splay\) . 这就是 空间局部性
于是我们整体归纳一下:
1>
刚刚被访问的元素极有可能再次被访问
2>
刚刚被访问的元素旁极有可能放着下一个被访问的元素
而我们的 \(Splay\) , 使用了这一原理,虽然可能单独的复杂度是 \(O(n)\) , 但均摊为 \(O(\log n)\)
Splay 用法
我们使用 \(E\) , 则节点 \(E\) 就会被上升到根:
因为两侧子树的结构在不断地调整,所以形象称之为伸展。但是好像进行单次的旋转,某些情况下复杂度依然高达 \(O(n)\)
我们来看最坏情况下的旋转5:
还是一条链!
这种情况如何伸展?
双层伸展
如果 \(x\) 和 \(fa_x\) 和 \(fa_{fa_x}\) 在同一条链上时,我们采用双层伸展,使其高度迅速减小
下面展示了一种因为节点更多,更加效率的树:
尽管 \(Splay\) 不像 \(AVL\) 和 红黑树这么严格,如果存在超深节点,就会因为 \(Splay\) 操作使得高度迅速减半。因此保证了整体的高效率。
具体实现
rotate : 其实很像 \(Treap\) 的,就是需要保存父亲。
template <typename T> void rotate(T x) {
int Grandfather = t[fath].father , Old_fa = fath ; bool d = Get_Direction(x) ;
t[Grandfather].son[Get_Direction(Old_fa)] = x , t[Old_fa].son[d] = t[x].son[d ^ 1] , t[x].son[d ^ 1] = Old_fa ;
Recognize_Father(Grandfather) ; Recognize_Father(Old_fa) ; Recognize_Father(x) ;
push_up(Old_fa) , push_up(x) ;
}
Splay : 注意一点细节即可。
void Splay(int x , int End) {
while (fath != End) {
int Old_fa = fath , Grandfather = t[fath].father ;
if (Grandfather != End) {
if (Get_Direction(x) == Get_Direction(Old_fa)) rotate(Old_fa) ;
else rotate(x) ;
}
rotate(x) ;
}
if (!End) root = x ;
}
一个很重要的事情:
你是否觉得Splay很高级很好用?那么我要告诉你,缺点就是:
常数巨大
Splay 的应用
由于旋旋旋旋,所以可以进行一点区间操作。
例: \([l , r]\) 将 \(l - 1\) 放置于根,然后将 \(r + 1\) 放到根的右儿子,那么根的右儿子的左子树就是这个区间。
呃呃呃,由于 \(Splay\) 常数巨大所以跑的有点小慢(⊙o⊙)…
例题: luoguP3380树套树
CODE
#include <bits/stdc++.h>
#define getchar() getchar_unlocked()
#define int long long
using namespace std ;
const int N = 4e5 + 10 ;
const int INF = 2147483647 ;
const int Size = 1e8 + 1 ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ;
while (c < '0' || c > '9') {
if (c == '-') f = -f ;
c = getchar() ;
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0' ;
c = getchar() ;
}
return x * f ;
}
int root[N * 10] ;
namespace SPLAY {
#define lson t[x].son[0]
#define rson t[x].son[1]
#define fath t[x].father
class Splay_Point_ {
public :
int son[2] , father , value , Size , cnt , pos ;
} t[N * 20] ; int numbol = 0 ;
void Print(int x) {
cerr << t[x].pos << ":\n" ;
cerr << t[lson].pos << ' ' << t[rson].pos << '\n' ;
if (lson) Print(lson) ;
if (rson) Print(rson) ;
}
int Create(int value) {
numbol ++ ;
t[numbol].cnt = t[numbol].Size = 1 ; t[numbol].value = value ;
return numbol ;
}
inline void push_up(int x) {
t[x].Size = t[lson].Size + t[rson].Size + t[x].cnt ;
}
inline bool Get_Direction(int x) {
return t[fath].son[1] == x ;
}
inline void Recognize_Father(int x) {
t[lson].father = t[rson].father = x ;
}
inline void rotate(int x) {
int Grandfather = t[fath].father , Old_fa = fath ; bool d = Get_Direction(x) ;
t[Grandfather].son[Get_Direction(Old_fa)] = x , t[Old_fa].son[d] = t[x].son[d ^ 1] , t[x].son[d ^ 1] = Old_fa ;
Recognize_Father(Grandfather) ; Recognize_Father(Old_fa) ; Recognize_Father(x) ;
push_up(Old_fa) , push_up(x) ;
}
void Splay(int x , int End , int Tree) {
while (fath != End) {
int Old_fa = fath , Grandfather = t[fath].father ;
if (Grandfather != End) {
if (Get_Direction(x) == Get_Direction(Old_fa)) rotate(Old_fa) ;
else rotate(x) ;
}
rotate(x) ;
}
if (!End) root[Tree] = x ;
}
void Insert(int x , int pos , int value , int Tree) {
int needfather = 0 ;
while (x && t[x].pos != pos) {
needfather = x ;
x = t[x].son[pos > t[x].pos] ;
}
if (x) {
t[x].Size ++ ; t[x].cnt ++ ; Splay(x , 0ll , Tree) ;
} else {
x = Create(value) ; fath = needfather ; t[fath].son[pos > t[fath].pos] = x ; t[x].pos = pos ;
Splay(x , 0ll , Tree) ;
}
}
int Rank(int root , int pos , int Tree) {
if (root == 0) return 0 ;
int x = root , ans = 0 , need = 0 ;
while (x) {
need = x ;
if (t[x].pos > pos) x = lson ;
else ans += t[x].cnt + t[lson].Size , x = rson ;
}
Splay(need , 0ll , Tree) ;
return ans ;
}
int Find(int root , int pos , int Tree) {
int x = root ;
while (x && t[x].pos != pos) x = t[x].son[pos > t[x].pos] ;
if (x == 0) return 0 ;
else {
Splay(x , 0ll , Tree) ; return x ;
}
}
int Precursor_Or_Subsquent(int x , bool d , int Tree) {
Splay(x , 0ll , Tree) ;
x = t[x].son[d] ;
while (t[x].son[d ^ 1]) x = t[x].son[d ^ 1] ;
return x ;
}
void Delete(int root , int val , int Tree) {
int x = Find(root , val , Tree) ;
if (!x) return ;
if (t[x].cnt > 1) {
t[x].cnt -- ; t[x].Size -- ;
Splay(x , 0ll , Tree) ;
return ;
}
int Pre = Precursor_Or_Subsquent(x , 0 , Tree) , Sub = Precursor_Or_Subsquent(x , 1 , Tree) ;
Splay(Pre , 0ll , Tree) ; Splay(Sub , Pre , Tree) ; t[Sub].son[0] = 0 ;
}
#undef lson
#undef rson
#undef fath
}
namespace SEGMENT_TREE {
#define lson t[id].son[0]
#define rson t[id].son[1]
#define mid ((l + r) >> 1)
using SPLAY :: Insert ;
using SPLAY :: Rank ;
using SPLAY :: Delete ;
using SPLAY :: Print ;
struct Tree_Point_ {
int son[2] ;
} t[N * 20] ; int numbol = 0 ;
int New_Code() {
++ numbol ;
Insert(root[numbol] , INF , INF , numbol) ; Insert(root[numbol] , -INF , INF , numbol) ;
return numbol ;
}
void updata(int &id , int l , int r , int x , int v) {
if (!id) id = New_Code() ;
Insert(root[id] , v , x , id) ;
if (l == r) return ;
if (x <= mid) updata(lson , l , mid , x , v) ;
else updata(rson , mid + 1 , r , x , v) ;
}
int GetRank(int id , int l , int r , int x , int y , int v) {
if (l == r) return 1 ;
if (v <= mid) return GetRank(lson , l , mid , x , y , v) ;
else {
int ans = Rank(root[lson] , y , lson) - Rank(root[lson] , x - 1 , lson) ;
return GetRank(rson , mid + 1 , r , x , y , v) + ans ;
}
}
int The_Kth(int id , int l , int r , int x , int y , int k) {
if (l == r) return l ;
int ans = Rank(root[lson] , y , lson) - Rank(root[lson] , x - 1 , lson) ;
if (ans >= k) return The_Kth(lson , l , mid , x , y , k) ;
else return The_Kth(rson , mid + 1 , r , x , y , k - ans) ;
}
void Delete_Tree(int id , int l , int r , int x , int v) {
Delete(root[id] , v , id) ;
if (l == r) return ;
if (x <= mid) Delete_Tree(lson , l , mid , x , v) ;
else Delete_Tree(rson , mid + 1 , r , x , v) ;
}
int Precursor(int id , int l , int r , int x , int y , int v) {
if (Rank(root[id] , y , id) - Rank(root[id] , x - 1 , id) == 0) return -INF ;
if (l == r && l == v) return -INF ;
if (l == r) return l ;
if (v <= mid) return Precursor(lson , l , mid , x , y , v) ;
else {
int ans = Precursor(rson , mid + 1 , r , x , y , v) ;
if (ans == -INF) return Precursor(lson , l , mid , x , y , v) ;
else return ans ;
}
}
int Subsquent(int id , int l , int r , int x , int y , int v) {
if (Rank(root[id] , y , id) - Rank(root[id] , x - 1 , id) == 0) return INF ;
if (l == r && l == v) return INF ;
if (l == r) return l ;
if (v >= mid + 1) return Subsquent(rson , mid + 1 , r , x , y , v) ;
else {
int ans = Subsquent(lson , l , mid , x , y , v) ;
if (ans == INF) return Subsquent(rson , mid + 1 , r , x , y , v) ;
else return ans ;
}
}
#undef lson
#undef rson
#undef mid
} using namespace SEGMENT_TREE ;
int n , m ; int a[N] , rad , opt ;
signed main() {
#ifndef ONLINE_JUDGE
freopen("1.in" , "r" , stdin) ;
freopen("1.out", "w" ,stdout) ;
#endif
n = read() , m = read() ;
for (int i = 1 ; i <= n ; ++ i) {
a[i] = read() ;
updata(rad , 0 , Size , a[i] , i) ;
}
int x , y , z ; int num = 0 ;
for (int i = 1 ; i <= m ; ++ i) {
opt = read() ; x = read() , y = read() ;
switch (opt) {
case 3 :
Delete_Tree(rad , 0 , Size , a[x] , x) ;
a[x] = y ;
updata(rad , 0 , Size , a[x] , x) ;
break ;
case 1 :
z = read() ;
printf("%lld\n" , GetRank(rad , 0 , Size , x , y , z)) ;
break ;
case 2 :
z = read() ;
printf("%lld\n" , The_Kth(rad , 0 , Size , x , y , z)) ;
break ;
case 4 :
z = read() ;
printf("%lld\n" , Precursor(rad , 0 , Size , x , y , z)) ;
break ;
case 5 :
z = read() ;
printf("%lld\n" , Subsquent(rad , 0 , Size , x , y , z)) ;
break ;
}
}
}
完结撒花 \(\color{pink}✿✿ヽ(°▽°)ノ✿\)
平衡树 -- Splay & Treap的更多相关文章
- UOJ#55. 【WC2014】紫荆花之恋 点分树 替罪羊树 平衡树 splay Treap
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ55.html 题解 做法还是挺容易想到的. 但是写的话…… 首先这种题如果只要求一棵树中的满足条件的点数( ...
- 数组splay ------ luogu P3369 【模板】普通平衡树(Treap/SBT)
二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) #include <cstdio> #define Max 100005 #define Inline _ ...
- hiho #1329 : 平衡树·Splay
#1329 : 平衡树·Splay 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho:小Hi,上一次你跟我讲了Treap,我也实现了.但是我遇到了一个关键的问题. ...
- 洛谷P3369 【模板】普通平衡树(Treap/SBT)
洛谷P3369 [模板]普通平衡树(Treap/SBT) 平衡树,一种其妙的数据结构 题目传送门 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除 ...
- [luogu P3369]【模板】普通平衡树(Treap/SBT)
[luogu P3369][模板]普通平衡树(Treap/SBT) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除x数(若有多个相同的数,因只删 ...
- Hihocoder 1329 平衡树·Splay(平衡树)
Hihocoder 1329 平衡树·Splay(平衡树) Description 小Ho:小Hi,上一次你跟我讲了Treap,我也实现了.但是我遇到了一个关键的问题. 小Hi:怎么了? 小Ho:小H ...
- AC日记——【模板】普通平衡树(Treap/SBT) 洛谷 P3369
[模板]普通平衡树(Treap/SBT) 思路: 劳资敲了一个多星期: 劳资终于a了: 劳资一直不a是因为一个小错误: 劳资最后看的模板: 劳资现在很愤怒: 劳资不想谈思路!!! 来,上代码: #in ...
- 平衡树——splay 二
上文传送门:平衡树--splay 一 - yi_fan0305 - 博客园 (cnblogs.com) OK,我们继续上文,来讲一些其他操作. 七.找排名为k的数 和treap的操作很像,都是通过比较 ...
- 平衡树——splay 一
splay 一种平衡树,同时也是二叉排序树,与treap不同,它不需要维护堆的性质,它由Daniel Sleator和Robert Tarjan(没错,tarjan,又是他)创造,伸展树是一种自调整二 ...
- 【BZOJ3224】Tyvj 1728 普通平衡树 Splay
Description 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数 ...
随机推荐
- C#数据结构与算法入门教程,值得收藏学习!
前言 最近看到DotNetGuide技术社区交流群有不少小伙伴提问:想要系统化的学习数据结构和算法,不知道该怎么入门,有无好的教程推荐的?,今天大姚给大家推荐2个开源.免费的C#数据结构与算法入门教程 ...
- 有手就会的 Java 处理压缩文件
@ 目录 前言 背景 第一步:编写代码 1.1 请求层 1.2 业务处理层 1.3 新增配置 第二步:解压缩处理 2.1 引入依赖 2.2 解压缩工具类 总结 前言 请各大网友尊重本人原创知识分享,谨 ...
- P3749 题解
既然是求最大值而且有收益有代价,所以考虑建立一个最大权封闭子图模型. 收益 正的美味值是收益,所以假若 \(d_{i,j} \geq 0\) 则建边 \((s,pos_{i,j},d_{i,j})\) ...
- 解码技术债:AI代码助手与智能体的革新之道
技术债 技术债可能来源于多种原因,比如时间压力.资源限制.技术选型不当等.它可以表现为代码中的临时性修补.未能彻底解决的设计问题.缺乏文档或测试覆盖等.虽然技术债可以帮助快速推进项目进度,但长期来看, ...
- 3.1 Y86-64指令集体系结构
程序员可见的状态 这里的程序员即可以是用汇编代码写程序的人,也可以是产生机器级代码的编译器.程序员可见的状态如下,有15个程序寄存器(%rax,%rbx等),三个一位的条件(ZF,OF,SF) ,程序 ...
- canvas绘制飞线效果
在我们做的可视化大屏项目中,经常会遇到飞线的效果. 在我们的大屏编辑器中,可以通过拖拽+配置参数的方式很快就能够实现.下面是我们使用大屏编辑器实现的一个项目效果: 中间地图就有飞线的效果. 抛开编辑器 ...
- oeasy教您玩转vim - 13 - # 大词小词
大词小词 回忆上节课内容 我们上次学习了 e e 代表 end 词尾 自有跳跃 还可以成倍次数的跳跃 但其实我是想以一个一个属性地跳跃,有没有方法呢? 查询帮助 没思路的话我们还是得继续查询 :h w ...
- [oeasy]python0080_设置RGB颜色_24bit_24位真彩色_颜色设置
RGB颜色 回忆上次内容 上次 首先了解了 索引颜色 \33[38;5;XXXm 设置 前景为索引色 \33[48;5;XXXm 设置 背景为索引色 RGB每种颜色 可选0-5 总共 6 级 想用 精 ...
- vue3基础学习
第一章:vue3.0基础 1,认识vue3.0 vue3.0发布时间为2020-9-18,从项目体验上,vue3.0比起vue2.0有以下优势: 打包大小减少41% 初次渲染块55%,更新渲染块133 ...
- stream的优化:java封装的拆箱与装箱的弊端
authors.stream() .map(author->author.getAge) .map(age->age+10)//Stream<Integer> .filter( ...