[学习笔记] 伸展树splay详解+全套模板+例题[Luogu P3369 【模板】普通平衡树]

文章目录

  • 引入概念
  • 全套模板
    • 变量声明
    • update
    • ==rotate旋转==
    • splay操作
    • insert插入
    • delete删除
    • 查找x的位置
    • 查找第k大
    • 前驱/后继
  • 极小值-inf和极大值inf的作用
  • 例题:P3369 【模板】普通平衡树
    • 题目
    • code

声明一下,许多代码的注解都在模板代码里面写了的,所以正文可能不会很多
其次是splaysplaysplay很多操作treaptreaptreap我都已经详解过了,只需要掌握不一样的旋转板块即可

引入概念

在这之前大家要了解二叉搜索树或者treap再或者非旋treap,也可以不了解,我会再次尽全力详细的给大家讲懵splay
在这里插入图片描述

二叉搜索树:是一种数据结构,每个点都存有各自的键值,按中序遍历这棵树,按键值生成的序列是有序的

显而易见对于给定的序列nnn,它的二叉搜索树不是唯一的
煮个栗子:123451\ 2\ 3\ 4\ 51 2 3 4 5,就能画出很多不一样的二叉搜索树
在这里插入图片描述


伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(logn)O(logn)O(logn)内完成插入、查找和删除操作
它由丹尼尔·斯立特Daniel Sleator 和 罗伯特·恩卓·塔扬Robert Endre Tarjan在1985年发明的

在伸展树上的一般操作都基于伸展操作:
假设想要对一个二叉查找树执行一系列的查找操作,为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置
于是想到设计一个简单方法:
在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方
伸展树应运而生:
伸展树是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去
它的优势在于不需要记录用于平衡树的冗余信息

假设想要对一个二叉查找树执行一系列的查找操作
为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置
于是想到设计一个简单方法:
在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方
splay tree应运而生。splay tree是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去

———————百度百科老师亲身授课,讲懵一群中华少年

这张图太好看了,忍不住盗过来
在这里插入图片描述
在这里插入图片描述


重点的就是模板,模板的原理会在该模板板块介绍,不要慌~~

全套模板


变量声明

我用的是结构体treetreetree,方便学习LCTLCTLCT(暴露了) 后面的封装
tree[i].valtree[i].valtree[i].val:表示该点的值
tree[i].cnttree[i].cnttree[i].cnt:表示该点在树上的出现次数
tree[i].siztree[i].siztree[i].siz:表示该点的子树大小,包括自己在内
tree[i].ftree[i].ftree[i].f:表示该点的爸爸(诶真乖
tree[i].son[2]tree[i].son[2]tree[i].son[2]:表示该点的两个儿子:son[0]son[0]son[0]左儿子,son[1]son[1]son[1]右儿子


这个没有什么值得讲的,不同的题肯定会有添加或更改,比如最大值就应该写成
tree[x].maxx=max(tree[tree[x].son[0]].maxx,tree[tree[x].son[1]].maxx,tree[x].val)tree[x].maxx = max(tree[tree[x].son[0]].maxx,tree[tree[x].son[1]].maxx,tree[x].val)tree[x].maxx=max(tree[tree[x].son[0]].maxx,tree[tree[x].son[1]].maxx,tree[x].val)
这里以求和为例

update

void update ( int x ) {tree[x].siz = tree[tree[x].son[0]].siz + tree[tree[x].son[1]].siz + tree[x].cnt;
}

rotate旋转

treaptreaptreap期间我们了解了单旋转(只旋一次),但是splaysplaysplay则是用双旋
接着因为是二叉树,双旋就分为了两种情况,直线型旋转和折线型旋转


直线型旋转,即三点成一条直线
在这里插入图片描述
这种情况的旋转规则:先旋转父亲,再旋转自己
在这里插入图片描述


折线型旋转
在这里插入图片描述
这种情况的旋转规则:旋转完自己,再旋转自己(自转两次)
在这里插入图片描述


总结一张图:
在这里插入图片描述

void rotate ( int x ) {//x是要旋转的点 int fa = tree[x].f;//x的父亲(father缩写) int Gfa = tree[fa].f;//x的祖父/fa的父亲(grandfather缩写(*^__^*))int k = ( tree[fa].son[1] == x );//x是fa的哪一个儿子 0左儿子 1右儿子if( Gfa) tree[Gfa].son[tree[Gfa].son[1] == fa] = x;//儿子非要当爹 取代了爹原来在祖父下的位置tree[x].f = Gfa; tree[fa].son[k] = tree[x].son[k ^ 1];if( tree[x].son[k ^ 1] ) tree[tree[x].son[k ^ 1]].f = fa;tree[x].son[k ^ 1] = fa;tree[fa].f = x;update ( fa );//别忘了更新信息update ( x );
}//0^1=1 1^1=0 其实也可以用取反(!)代替 

splay操作

我们使用双旋的做法,因为如果单旋将xxx旋到想要的位置,毒瘤会卡到我们n2n^2n2
那么如果想旋转到根的话,可以给第二个参数传0

void splay ( int x, int goal ) {//将x旋转到goal的儿子 如果goal是0意味着将x转到根while ( tree[x].f != goal ) {int fa = tree[x].f, Gfa = tree[fa].f;if ( Gfa != goal )//如果fa不是根节点就是两类(直线 折线)旋转( ( tree[Gfa].son[0] == fa ) ^ ( tree[fa].son[0] == x ) ) ? rotate ( x ) : rotate ( fa );//有点技巧但也很好理解 前两坨^=0就是直线的意思rotate ( x );}if ( ! goal )root = x;//如果goal是0 将根节点更新为x
}

insert插入

先用个动图直观感受一下
在这里插入图片描述
treaptreaptreap是孪生兄弟,从根开始,根据值的大小比较判断是往左走(x<tree[root].valx<tree[root].valx<tree[root].val)还是往右走(x>tree[root].valx>tree[root].valx>tree[root].val)

void insert ( int x ) {int u = root, fa = 0;//当前位置u及u的父节点fwhile ( u && tree[u].val != x ) {//仍有点且并未移动到想要的值 fa = u;u = tree[u].son[x > tree[u].val];//x大于当前点的值就在右儿子里面找 否则向左找 }if ( u ) //已经建过x这个值的位置了 tree[u].cnt ++;else {u = ++ Size;//新节点的位置 if ( fa ) tree[fa].son[x > tree[fa].val] = u;tree[u].son[0] = tree[u].son[1] = 0;//新点目前肯定没有儿子tree[u].val = x;tree[u].f = fa;tree[u].cnt = tree[u].siz = 1;}splay ( u, 0 );//把当前位置移到根保证结构平衡 因为前面更改了子树大小必须splay去update保证siz的正确 
}

delete删除

思路是首先分别找到xxx的前驱p1p1p1和后继p2p2p2,那么在当前树上就满足p1<x<p2p1<x<p2p1<x<p2并且中间没有其它数
很妙的就是我们把p1p1p1旋转到根,此时所有值比p1p1p1的都在右子树,然后把p2p2p2旋转到p1p1p1的儿子处,此时p2p2p2的左儿子就是xxx且只有一个,因为p2p2p2的左子树要满足>p1>p1>p1<p2<p2<p2,显而易见因为定义这里面只能插xxx,那么直接对p2p2p2的左子树进行操作即可

void Delete ( int x ) {int pre = PreSuf ( x, 0 ), suf = PreSuf ( x, 1 );splay ( pre, 0 );splay ( suf, pre );//pre有可能为0 但这个时候suf就应该旋转到根int u = tree[suf].son[0];if ( tree[u].cnt > 1 ) {tree[u].cnt --;splay ( u, 0 );}elsetree[suf].son[0] = 0, splay( suf, 0 );
}

查找x的位置

我相信给个图,大家就懂了
在这里插入图片描述
在这里插入图片描述
与插入是一个意思,此处就不过多解释

void find ( int x ) {//查找x的位置并旋转到根节点 if ( ! root ) //树是空的return;int u = root;while ( tree[u].son[x > tree[u].val] && x != tree[u].val )//存在儿子并且当前点不是我们要找的 u = tree[u].son[x > tree[u].val];splay ( u, 0 ); 
}

查找第k大

不要多说废话了,不理解可以移步上面的treaptreaptreap讲解

int findkth ( int x ) {if ( tree[root].siz < x )//整棵树的大小都没有k即不存在 return -1;int u = root;while ( 1 ) {if ( x <= tree[tree[u].son[0]].siz )u = tree[u].son[0];else if ( x <= tree[u].cnt + tree[tree[u].son[0]].siz )return u;else {x -= ( tree[tree[u].son[0]].siz + tree[u].cnt );u = tree[u].son[1];}}
}

前驱/后继

前驱后继的思路很妙,我们以前驱为例,把xxx旋到根,那么左子树就是比xxx小的,然后就在左儿子里面一直往右儿子走,likethis↓like\ this↓like this
在这里插入图片描述
但是如果树上没有我们要找的xxx,怎么办呢,这个时候的树根究竟是什么,根据我们findfindfind的原理写法,可以知道我们一定是找的最接近于xxx的值,不是它的前驱就是它的后继,那么这个时候根就有可能是答案
我们就在findfindfind后加入两个特判

int PreSuf ( int x, int f ) {//f=0找前驱 f=1找后继 find ( x );//查找后因为splay此时树根就是要查询节点if ( tree[root].val > x && f )//如果当前节点的值大于x并且要查找的是后继因为find原因可以直接返回了 return root;if ( tree[root].val < x && ! f )//与找后继同理 return root;int u = tree[root].son[f];if ( ! u )return 0;while ( tree[u].son[f ^ 1] )u = tree[u].son[f ^ 1];return u;
}

极小值-inf和极大值inf的作用

在没看到这个之前,如果你就拿着模板跑了,恭喜你流失了一天甚至更多的青春
因为上述模板都是在插入了哨兵的前提下才能运行的接下来让本蒟蒻来给你错误的讲讲哨兵的优秀

如果有哨兵存在,那么这些点永远都不会是死在最前面或者死的时候垫在最下面,就帮助我们少考虑很多边界,我昨天没有加哨兵,不停地补刀做手术,还是千疮百孔,病很多都是并发症,医不过来,加了个哨兵,自己就好了

最后简单提一下封装的好处,显然就是整个在一坨,方便整体移动和调试。代码分层也很清晰。可以用结构体

struct node {里面放所有splay的操作
}T;
调用函数需要写成 T.insert() 之类的

还可以

namespace splay {里面放所有splay操作
}
调用函数需要写成 splay :: insert() 之类的

通常是题目解法涉及到多种算法时,常按算法将各自模板进行封装。这样你可以很清楚地知道某个函数是属于哪一层的算法。

例题:P3369 【模板】普通平衡树

题目

送你离开千里之外

code

#include <cstdio>
#define maxn 100005
#define INF 0x7f7f7f7f
struct node {int f, cnt, val, siz, son[2];
}tree[maxn];
int n, Size, root;void update ( int x ) {tree[x].siz = tree[tree[x].son[0]].siz + tree[tree[x].son[1]].siz + tree[x].cnt;
}void rotate ( int x ) { int fa = tree[x].f; int Gfa = tree[fa].f;int k = ( tree[fa].son[1] == x );tree[Gfa].son[tree[Gfa].son[1] == fa] = x;tree[x].f = Gfa; tree[fa].son[k] = tree[x].son[k ^ 1];tree[tree[x].son[k ^ 1]].f = fa;tree[x].son[k ^ 1] = fa;tree[fa].f = x;update ( fa );update ( x );
}void splay ( int x, int goal ) {while ( tree[x].f != goal ) {int fa = tree[x].f, Gfa = tree[fa].f;if ( Gfa != goal )( ( tree[Gfa].son[0] == fa ) ^ ( tree[fa].son[0] == x ) ) ? rotate ( x ) : rotate ( fa );rotate ( x );}if ( ! goal )root = x;
}void insert ( int x ) {int u = root, fa = 0;while ( u && tree[u].val != x ) {fa = u;u = tree[u].son[x > tree[u].val]; }if ( u ) tree[u].cnt ++;else {u = ++ Size; if ( fa ) tree[fa].son[x > tree[fa].val] = u;tree[u].son[0] = tree[u].son[1] = 0;tree[u].val = x;tree[u].f = fa;tree[u].cnt = tree[u].siz = 1;}splay ( u, 0 );
}void find ( int x ) {if ( ! root )return;int u = root;while ( tree[u].son[x > tree[u].val] && x != tree[u].val )u = tree[u].son[x > tree[u].val];splay ( u, 0 ); 
}int PreSuf ( int x, int f ) { find ( x );if ( tree[root].val > x && f )return root;if ( tree[root].val < x && ! f )return root;int u = tree[root].son[f];if ( ! u )return 0;while ( tree[u].son[f ^ 1] )u = tree[u].son[f ^ 1];return u;
}void Delete ( int x ) {int pre = PreSuf ( x, 0 ), suf = PreSuf ( x, 1 );splay ( pre, 0 );splay ( suf, pre );int u = tree[suf].son[0];if ( tree[u].cnt > 1 ) {tree[u].cnt --;splay ( u, 0 );}elsetree[suf].son[0] = 0;
}int findkth ( int x ) {if ( tree[root].siz < x )return -1;int u = root;while ( 1 ) {if ( x <= tree[tree[u].son[0]].siz )u = tree[u].son[0];else if ( x <= tree[u].cnt + tree[tree[u].son[0]].siz )return u;else {x -= ( tree[tree[u].son[0]].siz + tree[u].cnt );u = tree[u].son[1];}}
}int main() {insert ( INF );insert ( -INF );scanf ( "%d", &n );int opt, x;for ( int i = 1;i <= n;i ++ ) {scanf ( "%d %d", &opt, &x );switch ( opt ) {case 1 : insert ( x ); break;case 2 : Delete ( x ); break;case 3 : {find ( x );printf ( "%d\n", tree[tree[root].son[0]].siz );break;}case 4 : {int u = findkth ( x + 1 );printf ( "%d\n", tree[u].val );break;}case 5 : {int u = PreSuf ( x, 0 );printf ( "%d\n", tree[u].val );break;}case 6 : {int u = PreSuf ( x, 1 );printf ( "%d\n", tree[u].val );break;}}}return 0;
}
#include <cstdio>
#define maxn 100005
#define INF 0x7f7f7f7f
struct SplayTree {struct node {int f, cnt, val, siz, son[2];void init ( int Val, int fa ) {val = Val;cnt = siz = 1;f = fa;son[0] = son[1] = 0;}}tree[maxn];int root, Size;void update ( int x ) {tree[x].siz = tree[tree[x].son[0]].siz + tree[tree[x].son[1]].siz + tree[x].cnt;}void rotate ( int x ) { int fa = tree[x].f; int Gfa = tree[fa].f;int k = ( tree[fa].son[1] == x );tree[Gfa].son[tree[Gfa].son[1] == fa] = x;tree[x].f = Gfa; tree[fa].son[k] = tree[x].son[k ^ 1];tree[tree[x].son[k ^ 1]].f = fa;tree[x].son[k ^ 1] = fa;tree[fa].f = x;update ( fa );update ( x );}void splay ( int x, int goal ) {while ( tree[x].f != goal ) {int fa = tree[x].f, Gfa = tree[fa].f;if ( Gfa != goal )( ( tree[Gfa].son[0] == fa ) ^ ( tree[fa].son[0] == x ) ) ? rotate ( x ) : rotate ( fa );rotate ( x );}if ( ! goal )root = x;}void insert ( int x ) {int u = root, fa = 0;while ( u && tree[u].val != x ) {fa = u;u = tree[u].son[x > tree[u].val]; }if ( u ) tree[u].cnt ++;else {u = ++ Size; if ( fa ) tree[fa].son[x > tree[fa].val] = u;tree[u].son[0] = tree[u].son[1] = 0;tree[u].val = x;tree[u].f = fa;tree[u].cnt = tree[u].siz = 1;}splay ( u, 0 );}void find ( int x ) {if ( ! root )return;int u = root;while ( tree[u].son[x > tree[u].val] && x != tree[u].val )u = tree[u].son[x > tree[u].val];splay ( u, 0 ); }int PreSuf ( int x, int f ) { find ( x );if ( tree[root].val > x && f )return root;if ( tree[root].val < x && ! f )return root;int u = tree[root].son[f];if ( ! u )return 0;while ( tree[u].son[f ^ 1] )u = tree[u].son[f ^ 1];return u;}void Delete ( int x ) {int pre = PreSuf ( x, 0 ), suf = PreSuf ( x, 1 );splay ( pre, 0 );splay ( suf, pre );int u = tree[suf].son[0];if ( tree[u].cnt > 1 ) {tree[u].cnt --;splay ( u, 0 );}elsetree[suf].son[0] = 0;}int findkth ( int x ) {if ( tree[root].siz < x )return -1;int u = root;while ( 1 ) {if ( x <= tree[tree[u].son[0]].siz )u = tree[u].son[0];else if ( x <= tree[u].cnt + tree[tree[u].son[0]].siz )return u;else {x -= ( tree[tree[u].son[0]].siz + tree[u].cnt );u = tree[u].son[1];}}}}T;
int n;int main() {T.insert ( -INF );T.insert ( INF );scanf ( "%d", &n );int opt, x;for ( int i = 1;i <= n;i ++ ) {scanf ( "%d %d", &opt, &x );switch ( opt ) {case 1 : T.insert ( x ); break;case 2 : T.Delete ( x ); break;case 3 : {T.find ( x );printf ( "%d\n", T.tree[T.tree[T.root].son[0]].siz );break;}case 4 : {int u = T.findkth ( x + 1 );printf ( "%d\n", T.tree[u].val );break;}case 5 : {int u = T.PreSuf ( x, 0 );printf ( "%d\n", T.tree[u].val );break;}case 6 : {int u = T.PreSuf ( x, 1 );printf ( "%d\n", T.tree[u].val );break;}}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/317993.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手写AspNetCore 认证授权代码

在普通的MVC项目中 我们普遍的使用Cookie来作为认证授权方式&#xff0c;使用简单。登录成功后将用户信息写入Cookie&#xff1b;但当我们做WebApi的时候显然Cookie这种方式就有点不适用了。在dotnet core 中 WebApi中目前比较流行的认证授权方式是Jwt (Json Web Token) 技术。…

YBTOJ:采矿战略(线段树维护dp、树链剖分)

文章目录题目描述解析代码题目描述 所谓线段树维护dp&#xff0c;就是在线段树上维护dp &#xff08;逃&#xff09; 解析 把树剖一下后就变成了区间问题 考虑建一棵线段树&#xff0c;每一个结点都是一个背包 这样就能区间查询&#xff0c;也能带修了 这种做法复杂度其实并不…

【用皇宫三十六计生存法则带你走进LCT(动态树)】LCT概念+模板+例题【洛谷P3690 Link Cut Tree(动态树)】

文章目录LCT概念模板rotatoisrootsplayaccessmakerootsplitfindrootlinkcut封装版例题题目code普通版code封装版这篇博客主要是帮助大家理解各个模板及LCTLCTLCT的意思&#xff0c;方便理解&#xff0c;模板写法的理解在代码里有注释详解&#xff0c;如果要看原理的话&#xff…

迈向现代化的 .Net 配置指北

1. 欢呼 .NET Standard 时代我现在已不大提 .Net Core&#xff0c;对于我来说&#xff0c;未来的开发将是基于 .NET Standard&#xff0c;不仅仅是 面向未来 &#xff0c;也是 面向过去&#xff1b;不只是 .Net Core 可以享受便利&#xff0c; .NET Framework 不升级一样能享受…

YBTOJ洛谷P2042:维护数列(平衡树)

文章目录题目描述解析删除区间插入数列修改&翻转区间和&最大子段和代码传送门题目描述 解析 阴间题… 这不是裸的板子吗&#xff1f; 国赛真的有人能把这题写出来吗… 应该算一道练习作用很强的题了 写完这题&#xff0c;各种平衡树维护区间操作的方法可以说是毕业了吧…

CAP 2.4版本发布,支持版本隔离特性

前言自从上次 CAP 2.3 版本发布 以来&#xff0c;已经过去了几个月的时间&#xff0c;这几个月比较忙&#xff0c;所以也没有怎么写博客&#xff0c;趁着2019年到来之际&#xff08;现在应该是2019年开始的时候&#xff09;&#xff0c;CAP也发布了2018年的最后一个大版本 2.4&…

【周末狂欢赛7】【NOIP模拟赛】七夕祭,齿轮(dfs),天才黑客

文章目录T1题目题解codeT2题目题解codeT3题目题解codeT1 题目 七夕节因牛郎织女的传说而被扣上了「情人节」的帽子。于是TYVJ今年举办了一次线下七夕祭。Vani同学今年成功邀请到了cl同学陪他来共度七夕&#xff0c;于是他们决定去TYVJ七夕祭游玩。 TYVJ七夕祭和11区的夏祭的…

.NET Core 如何为项目提供高性能解决方案?

本系列&#xff0c;我们将探讨.NET Core 的一些好处&#xff0c;以及它如何为市场提供高性能解决方案&#xff0c;为传统.NET 开发人员和技术人员提供帮助。正文前言随着.NET Core 2.0 在 2016 年首次发布&#xff0c;微软拥有了这个通用、模块化、跨平台开源项目的下一个主要版…

[2.9训练]【CF909C】Python Indentation,【CF909D】Colorful Points,【CF909E】Coprocessor

文章目录T1&#xff1a;Python Indentation题目题解codeT2&#xff1a;Colorful Points题目题解codeT3&#xff1a;Coprocessor题目题解codeT1&#xff1a;Python Indentation 题目 题目描述 In Python, code blocks don’t have explicit begin/end or curly braces to mark…

Docker最全教程之使用Tencent Hub来完成CI(十)

本周更新两篇&#xff0c;保证不太监&#xff01;在本系列教程中&#xff0c;笔者希望将必要的知识点围绕理论、流程&#xff08;工作流程&#xff09;、方法、实践来进行讲解&#xff0c;而不是单纯的为讲解知识点而进行讲解。也就是说&#xff0c;笔者希望能够让大家将理论、…

[2.7]【CF933A】A Twisty Movement【CF926B】Add Points【CF917A】The Monster【CF919E】Congruence Equation

文章目录T1&#xff1a;A Twisty Movement题目题解codeT2&#xff1a;Add Points题目题解codeT3&#xff1a;The Monster题目题解codeT4&#xff1a;Congruence Equation题目题解codeT1&#xff1a;A Twisty Movement 题目 题目 题解 因为aia_iai​1/21/21/2&#xff0c;于…

LIS最长上升子序列

LIS算是比较经典的问题&#xff0c;常用的是O(n^2)的方法 for(int i1;i<n;i){dp[i]1;for(int j1;j<i;j){if(a[j]<a[i])dp[i]max(dp[i],dp[j]1);}mxmax(mx,dp[i]);}我们这里优化成O(nlogn) 我们模拟一个栈stack&#xff0c;每读入一个数&#xff0c;如果这个数大于栈顶…

EF Core 数据库 Provider 一览

当 EF Core 1.x 系列和 2.0 版本之间经过重大的重写时&#xff0c;所有 EF Core 数据库 Provider 都受到重创。从那时起&#xff0c;各种私人和商业开发团队一直在努力填补这个空白。正文当 EF Core 1.x 系列和 2.0 版本之间经过重大的重写时&#xff0c;所有 EF Core 数据库 P…

[3.3训练赛]One-Dimensional(矩阵快速幂),Freda的迷宫(无向图强连通分量+并查集),一道防AK好题

文章目录T1:One-DimensionaltitlesolutioncodeT2:【NOIP模拟赛】Freda的迷宫titlesolutioncodeT3:【NOIP模拟赛】一道防AK好题titlesolutioncode确实没想到自己写文章能隔这么久&#xff0c;鸽王预警 T1:One-Dimensional title 考虑一个含有 N 个细胞的一维细胞自动机。细胞…

牛客网专题 概率dp

文章目录概念&#xff1a;例题引入&#xff1a;解答&#xff1a;Happy Running NC15532题意&#xff1a;题解&#xff1a;代码&#xff1a;poj2096 NC106693 Collecting Bugs题意&#xff1a;题解&#xff1a;代码&#xff1a;NC210477 带富翁题意&#xff1a;题解&#xff1a;…

.NET Core 3.0 特性初探:C# 8、WPF、Windows Forms、EF Core

.NET Core 的下一个主要版本最近进入了预览阶段&#xff0c;.NET Core 3.0 将支持使用 Windows Presentation Foundation &#xff08;WPF&#xff09;、Windows Forms&#xff08;WinForms&#xff09;、Entity Framework &#xff08;EF&#xff09;、Blazor、 C# 8 和.NET S…

YBTOJ洛谷P4074:糖果公园(树上莫队)

文章目录解析update:代码所谓树上莫队&#xff0c;就是在树上的莫队 &#xff08;逃&#xff09; 传送门 解析 似乎就是树上的这道题 考虑如何转化为序列问题呢? 考虑dfs序 但是又一个问题。。。 似乎这条链的dfs序不连续啊 树剖一下就好啦 考虑更阳间的方法 求出这棵树的欧…

【用梨泰院class中的财阀世家带你洞悉替罪羊树】Scapegoat Tree原理,模板,例题

我想写在前面&#xff0c;本文财阀世家全是虚构&#xff0c;没有诋毁之意&#xff0c;如有雷同&#xff0c;纯属巧合 红色预警&#xff01;&#xff01;&#xff01;红色预警 文章目录Scapegoat Tree概念模板变量声明Bad函数判断是否需要重构理解模板rebuild重构理解模板inser…

领域驱动设计,让程序员心中有码(五)

1 从搬砖谈领域对象有一个古老的故事&#xff0c;大概是这样的。作者问三个建筑工地上的工人他们在干什么&#xff1f;有一个没精打采的说&#xff0c;我在挖洞&#xff01;而另一一个人却说&#xff0c;我在盖一座房子。还有一个人说&#xff0c;我在建立一座巨大的城市。…

.NET Core实战项目之CMS 第十四章 开发篇-防止跨站请求伪造(XSRF/CSRF)攻击处理...

通过 ASP.NET Core&#xff0c;开发者可轻松配置和管理其应用的安全性。 ASP.NET Core 中包含管理身份验证、授权、数据保护、SSL 强制、应用机密、请求防伪保护及 CORS 管理等等安全方面的处理。 通过这些安全功能&#xff0c;可以生成安全可靠的 ASP.NET Core 应用。而我们这…