数据结构小节(上)

Posted poweryao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构小节(上)相关的知识,希望对你有一定的参考价值。

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

$$$$

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)\)的区间。于是类似于最大字段和的搞法搞一波就行。

以上是关于数据结构小节(上)的主要内容,如果未能解决你的问题,请参考以下文章

MIDI,如何获取小节和音符

web安全小节

menu小节

数论小节

你可能不知道的JavaScript代码片段和技巧(上)

Wagtail - 在页面上呈现带有相关片段和标签的数据时遇到问题