数据结构小节(上)

蒟蒻最近学习了一些数据结构,下面是蒟蒻的总结。

$$$$

1.线段树合并

所谓线段树合并,字面上理解,就是将两颗线段树合并在一起,所以多用于权值

线段树,而且多在 树形结构 的题中出现。然而对两颗满二叉树的合并一次复杂

度会达到\(O(nlog_2n)\)

对于总操作\(m\),一般来说每次就是动态开点复杂度\(O(mlog_2n)\)。然后考虑每

次合并的活,我们每一次只会把 有的 都加起来,接着递归向下处理,把 没有

的直接接到新的线段树上。所以复杂度其实是关乎重叠部分的。如果重叠的越

多,所用的时间也越多。但是我们又可以把每一次操作想象成总点数减去此次操

作中两棵线段树重叠部分的大小,把其全部减完就结束。而总点数只有\(mlog_2n\),

所以最终的复杂度还是正确的。

1.P4556 [Vani有约会]雨天的尾巴

题意:在一棵树上支持在链上的每个点插入一个值。询问所有点中最多的值是哪

一个

树上差分加线段树合并,维护\(max\)

核心代码:

void meg(int x , int y , int l , int r ){
    if( l == r ){
        mx[x] += mx[y] ;
        return ;
    }
    int mid = ( l + r ) >> 1 ;
    if( ls[x] && ls[y] ) meg( ls[x] , ls[y] , l , mid ) ;
    else ls[x] += ls[y] ;
    if( rs[x] && rs[y] ) meg( rs[x] , rs[y] , mid + 1 , r ) ;
    else rs[x] += rs[y] ;
    pushup(x) ; return ;
}
2.P3521 [POI2011]ROT-Tree Rotations

题意:给一棵n(1≤n≤200000个叶子的二叉树,可以交换每个点的左右子树,

要求前遍历叶子的逆序对最少。

这一道题读入有点恶心。。。。。

其实这题有点类似于分治,对于一棵子树,我们只要算出来它的左右儿子两颗

子树独立开来的逆序对个数,和这两颗子树合在一起时的最小逆序对个数。对

于一个节点后面的这个操作,其余的递归处理,然后值域线段树合并,并计算

逆序对,对于交换和不交换,取个\(min\)。

代码:

void merge(int x , int y , int l , int r ){
    if( l == r ){
        siz[x] += siz[y] ;
        return ;
    }
    int mid = ( l + r ) >> 1 ;
    res1 += (ll)siz[ls[x]] * siz[rs[y]] ;
    res2 += (ll)siz[ls[y]] * siz[rs[x]] ;
    if( ls[x] && ls[y] ) merge( ls[x] , ls[y] , l , mid ) ;
    else ls[x] += ls[y] ;
    if( rs[x] && rs[y] ) merge( rs[x] , rs[y] , mid + 1 , r ) ;
    else rs[x] += rs[y] ;
    siz[x] = siz[ls[x]] + siz[rs[x]] ;
}
ll dfs2(int x){
    if( book[x] ) return 0 ;
    ll ans = 0 ;
    ans += dfs2( tl[x] ) ; ans += dfs2( tr[x] ) ;
    res1 = 0 ; res2 = 0 ;
    merge( root[x] , root[tl[x]] , 1 , n ) ; merge( root[x] , root[tr[x]] , 1 , n ) ;
    ans += min( res1 , res2 ) ;
    return ans ;
}
3.P5298 [PKUWC2018]Minimax

题意:自己去看看

后面式子的一些玩意直接费马小定理求逆元,把小数变整数。

然后显然可以发现,树上结点都是又可能被选上来的其实这个上面那题合并的

处理方法差不太多。也是考虑合并上来的左右两颗子树上的值域,右值域比较

左边的值域,乘概率。

大概长这样:

void mul(int x ,int k){
    sum[x] =  1ll * sum[x] * k % Mod ;
    lazy[x] = 1ll * lazy[x] * k % Mod ;
    return ;
}
void pushdown(int x){
    if( lazy[x] == 1 ) return ;
    if( ls[x] ) mul( ls[x] , lazy[x] % Mod ) ;
    if( rs[x] ) mul( rs[x] , lazy[x] % Mod ) ;
    lazy[x] = 1 ; return ;
}
void build(int &x , int l , int r ,int pos ){
    x = ++cnt ;
    lazy[x] = 1 ;
    sum[x] = 1 ;
    if( l == r ) return ;
    int mid = ( l + r ) >> 1 ;
    if( pos <= mid ) build( ls[x] , l , mid , pos ) ;
    else build( rs[x] , mid + 1 , r , pos ) ;
    return ;
}
int merge(int x , int y , int l , int r , ll a , ll b ){
    if( !x ){ mul( y , a ) ; return y ; }
    if( !y ){ mul( x , b ) ; return x ; }
    int mid = ( l + r ) >> 1 ;
    pushdown( x ) ; pushdown(y) ;
    ll sumx0 = sum[ls[x]] , sumx1 = sum[rs[x]] , sumy0 = sum[ls[y]] , sumy1 = sum[rs[y]] ;
    ls[x] = merge( ls[x] , ls[y] , l , mid , 1ll * ( a+sumx1*(1-p) )%Mod , 1ll * ( b+sumy1*(1-p) )%Mod ) ;
    rs[x] = merge( rs[x] , rs[y] , mid + 1 , r ,1ll * ( a+sumx0*p )%Mod , 1ll * ( b+sumy0*p )%Mod ) ;
    sum[x] = ( sum[ls[x]] + sum[rs[x]] ) % Mod ;
    return x ;
}
4.COT3

博弈论SG函数好题,这其实是01tire树合并,本质上和线段树合并是一样的。

然后这题再套一个tire树求\(mex\),tire树\(xor\)就行

2.启发式合并

启发式合并其实就是根据人类自己的主观意识的一种算法。

1.P3201 [HNOI2009]梦幻布丁

你会发现这一题它是将所有的颜色变成另外一种颜色,也就是把一些点都染成

同一种相对于其它点的其它颜色。要你求颜色段树。你把这两种颜色都变成其

中一种都是可以的。答案不会变。所以我们想每次合并,就将小的合并到大的

中去。容易想象这样的复杂度是\(O(nlog_2n)\)的。

2.P3302 [SDOI2013]森林

好的一题。做这道题可以先去做一下不带修改的COT。

这道题其实就是待修改,强制在线。其实这也类似,首先,和COT一样,建树

递归差分主席树维护根到点的信息。然后连边就是合并两棵树,这样就直接将

小的那颗子树合并到大的中去。暴力重构整棵树。复杂度\(O(nlog_2^2n)\)

3.平衡树

这玩意一直都不太熟,到现在还只会splay,不会别的。

首先,splay的本质是一个二叉查找树。也就是说,它是靠某种权值的大小关系

进行建树的。可以是权值,可以是区间上的位置关系。

1. P3369 【模板】普通平衡树(维护权值大小)
2. P3391 【模板】文艺平衡树(Splay)(维护区间上的位置)

(铭记标记打在一个点上表示左右子树的子树需要交换)

  1. 线段树1,线段树2都可以用平衡树解决,类似于文艺平衡树
4. 入门题:P2286 [HNOI2004]宠物收养场,P2234 [HNOI2002]营业额统计
5. 裸一点的:P1110 [ZJOI2007]报表统计
6. P3765 总统选举:

这道题一开始卡了卡我,后来某神告诉我要用这个

P2397 yyy loves Maths VI (mode)里面的一个结论。

然后区间线段树维护区间和。来维护一下如果众数大于n/2的人。

接着维护n棵平衡树,维护选择i的每个人的结点。是否真的大于n/2

7. P2042 [NOI2005]维护数列

码农题。

卡常卡空间。然后区间平衡树维护区间\(dp\)。

8. P3960 [noip2017]列队

据说可以用树状数组(我不会)。

平衡树:

很容易发现,1-n行都会向左移动,而只有第m列会向上移动。所以我们可以维

护\(n+1\)棵平衡树。分别维护\(1-n\)行的1到m-1个人以及第\(m\)列。每次出去一个

人就是在第\(i\)棵平衡树上删掉\(kth (j)\)然后加入第\(n+1\)棵平衡树的第\(i\)个

数,,,,,,后面就懂了。。。。。。(滚粗)

但这里有个恶心的地方就是你不可以一个一个加进去。所以只可以把所有的点

当作一个大点,记录这个点的第一个点权,和一个\(siz\),然后要出列的人我们

进行分裂这个节点。

9.P3285 [SCOI2014]方伯伯的OJ

码农题。维护两个平衡树,排名编号。然后也要记得像上面一样,要分裂点。

10.CF878C Tournament

平衡树维护强连通。

其实这道题也不算实实在在的强连通。我们发现,这道题可以将每个人进行连

有向边,就是说,如果A有一项大于B,则将A连一条向B的边。因为这道题其实是

有性质可找的。如果没有加入操作,我们只要求一边强连通,缩点,求没有入

度的强连通的点数。那么我们现在可以来优化一下建图。如果存在强制关系,4

的所有项大于3,3的所有项大于2,2的所有项大于1

但如果新来一个点将他们都加入强连通分量。4到2的边和3到1的边就实在没有

必要存在。就是说我们知道下面那个图,就知道上面的那个关系。

所以,我们可以用一个平衡树来为维护他们的一个强制性大小关系。也就将整

幅图看作一条链了。每次维护强连通中中每一项的最大值和最小值。加入一个

点,找最左边可以被吊打的点l,和最右边可以吊打别人的点r。

1.如果l在r的左边,说明l到r都可以缩成一个强连通分量。

2.如果l在r的右边,说明它不可以缩出来一个点。

11. P4008 [NOI2003]文本编辑器

字符串的平衡树哦。本质上还是一个区间平衡树。

升级版就是 P4567 [AHOI2006]文本编辑器——多了一个旋转操作。

12. P4036 [JSOI2008]火星人

平衡树上维护hash值。思想还是很好懂得。。。。

4.主席树

安利一波主席树。主席树,由多个线段树套在一起达到节省空间目的的一类数据

结构,有点类似于前缀和这样的思想。

这是一些收藏题:

1.P4211 [LNOI2014]LCA

依然是一道好题,类似于我们刚开始学\(LCA\)时的思想,给每个点差分。。。。

注意的时这道题时主席树套树剖。

2.P2839 [国家集训队]middle

一道非常优秀的题,主席树万千道,做完大骂一声:主席树还可以这样玩。

之前一直\(naive\)的以为主席树只可以用于权值线段树。

我们考虑二分答案,如果这个点为中位数,或者可能更大,那么其满足的条件就

是比它大的个数(包括相等)和比它小的数的个数的差非负。

我们先给原数列排序,并记录原来存在的位置。每个点权对应一个只包含

\(1/(-1)\)的区间。于是类似于最大字段和的搞法搞一波就行。

原文地址:https://www.cnblogs.com/powerYao/p/11445277.html

时间: 2024-07-30 22:14:33

数据结构小节(上)的相关文章

数据结构-从上往下打印二叉树

题目:从上往下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印. 分析:其实就是按层的遍历方式 #include <iostream> #include <deque> using namespace std; struct BinaryTree{ int data; BinaryTree* lchild; BinaryTree* rchild; }; void PrintLeverTree(BinaryTree* root){ if(root == NULL){ ret

数据结构--书上代码用栈求解迷宫问题存在BUG(非最优解)

数据结构第四版p79页迷宫问题我觉得存在BUG,下图盗用贺老师就会的QAQ,也希望贺老师能看到帮忙解答一下啦. BUG:  程序从起始点(1,1)开始寻找路径,在当前点进行判断其上下左右是否存在可走点,如果从(1,1)点开始判断如图那么它的右(1,2)下(2,1)都是可走点那么将右边的格子坐标进栈呢还是将下边的格子坐标进栈?书本上给的代码是先判断上边格子再判断右边格子再判断下边格子再判断左边格子,这就造成了一个问题:(1,2)则个点会被进栈(因为(1,2)点位于(1,1)点的右边被先判断进栈),

数据结构小节(1)

接口总结 UML类图中的关系 依赖 : 局部变量和外部类的关系 关联 : 简单理解的话就是成员变量 聚合:has-a的成员变量,整体与部分分离,有各自独立的生命周期.eg: 公司&员工,电脑&cpu 合成: contains-a关系的成员变量,外部类和内部成员保持同一个生命周期 实现 : 实现类 实现→ 接口 比如Chinese实现Person 泛化 : 父类与子类的关系,继承为其中一种 集合的分类 集合主要被分为Collection和Map两个接口 Collection被List和Set

表达式求值(数据结构书上栈的应用之一)

主要内容:表达式求值,提交nyoj通过... 思路:主要就是一个开两个栈,然后一个操作符栈,一个操作数栈.. 我的代码如下(比较简洁): /***** Author Gery ******/ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<vector> #include<cmath&

表达式求值(数据结构书上栈的应用之中的一个)

主要内容:表达式求值.提交nyoj通过... 思路:主要就是一个开两个栈,然后一个操作符栈.一个操作数栈. . 我的代码例如以下(比較简洁): /***** Author Gery ******/ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<vector> #include<cma

初学者对于I/O流的小节-上

我是一个刚刚进入java开发的菜鸟,java 基础比较薄弱,面试的路程也经历坎坷.带着这样的心态,我总结了这个关于java io 的文章,肯定覆盖面不全,但是肯定都是最基础最实用的. 第一章 初步认识 输入输出流 I/O流 第一步 流是什么 ? 流是对于信息的一个抽象! 站在程序的角度上讲 输入流 是读取数据 站在程序的角度上讲 输出流 是写入数据 第二步 流 分两种 (1)字节流 用于译字节为单位的输入输出,主要是用于处理 图形声音文件 InputStream 这是所有字节输入流的 父类 Ou

数据结构书上的纵横图代码

#include <stdio.h> #include <stdlib.h> #define MAXSIZE 20 int main() { int m, magic[MAXSIZE][MAXSIZE] = {0}, i = 0,j, x, y; scanf("%d",&m); magic[i][m/2] = 1; x = 0;y =m/2; for(i = 2;i <= m*m ;i++)//我进行的优化及改进 { if(magic[(x-1+m

c数据结构 绪论 上

四种逻辑结构:1:集合结构 结构中的数据元素除了同属于同一个集合的关系外,无任何其他关系2:线性结构 结构中的数据元素之间存在着一对一的线性关系3:树形结构 结构中的数据元素之间存在着一对多的层次关系4:图状结构或网状结构 结构中的数据元素之间存在着多对多的任意关系 存储结构:1:顺序存储结构 用一组连续的存储单元依次存储结构元素,数据元素之间的逻辑关系由元素的存储位置来表示--c语言中数组实现 2:链式存储结构 用一组任意的存储单元存储数据元素,数据元素之间的逻辑关系用指针来表示--c语言中链

第一章 python中重要的数据结构(上)

最近,由于工作需要,使用python开发公司的运维自动化平台,所以找本书来并结合官方手册,开始python的学习之旅. 一.列表 [含义]:列表用中括号表示,通过逗号进行分隔一组数据(可以为不同的数据类型),如以下的声明: 1 >>> language = ['chinese','english','japanese'] 2 >>> contries = ['China','Amercia','England','Japan'] 3 4 >>> edw