蒟蒻最近学习了一些数据结构,下面是蒟蒻的总结。
$$$$
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,线段树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