题解:CF438D The Child and Sequence
题目
思路
题目要求我们完成三种操作:
- 区间求和
- 区间取模
- 单点修改
1,3两种都非常简单,问题在于如何维护区间取模。
可以想到维护区间最大值,如果小于模数则不处理,否则将这个区间重新pushup。
如何证明该复杂度?注意到,一个数最多会被取模 $log{a_i}$ 次,所以复杂度我 $O(nlog_nlog{\max{a_i}})$ 。
代码
1 |
|
然而没有什么技巧。
题解:CF438D The Child and Sequence
题目要求我们完成三种操作:
1,3两种都非常简单,问题在于如何维护区间取模。
可以想到维护区间最大值,如果小于模数则不处理,否则将这个区间重新pushup。
如何证明该复杂度?注意到,一个数最多会被取模 $log{a_i}$ 次,所以复杂度我 $O(nlog_nlog{\max{a_i}})$ 。
1 |
|
然而没有什么技巧。
题解:Black And White / Flip and Combos
题目描述
给你一个长度为n的01序列 a[1],…,a[n]
每次询问给定一个区间[l,r],询问[l,r]里最长的连续的1的长度
每次修改给定一个区间[l,r],将[l,r]里的a[i]变成1-a[i](或者说异或1)
n, m <= 100000
输入格式
第一行一个n
第二行n个数,表示01序列初始状态
第三行一个m,接下来m行,每行有三个数x,l,r
若 x=0,表示一次查询,查询[l,r]里的最长连续1的长度
若 x=1,表示一次修改,将[l,r]里的数异或1
输出格式
对于输入里的每一个查询,输出一行一个整数,该询问的最长连续1的长度
维护最长子串是经典的线段树,由于有01翻转修改,所以不能只存连续1串,也要存0.
思路可能和GSS有点像,都是维护区间最长前缀、后缀和子串(GSS是子序列),修改就翻转+重新pushup就行了。
其实不是很困难,但是线段树写起来非常易错(因为我很菜orz)
1 |
|
撒花!
又开天坑.mp4
做一下SPOJ的8道数据结构,但是什么时候能写完我也不知道qwq
这个很多帖子都有讲,主要就是google的recaptcha用不了,把它更换成recaptcha.net
的验证(虽然尝试跨越长城了,但是仍然用不了,不知道为啥)
解决方法就是安装 Gooreplacer(for Microsoft Edge)
下载完成后,点击橙色小刷子,在弹出的窗口中点击最上方的蓝色按钮“配置规则”。点击页面中表格上方的“新增”,在弹出的窗口内依次输入 www.google.com/recaptcha
,“通配符”,recaptcha.net/recaptcha
,点击“提交”。
这个比较简单,就是最基本的一个框架。
一个节点有4个部分:区间和sum、区间最大前缀和lmx,区间最大后缀和rmx,区间最大子段和mx
在算一个区间最大子段和时,会出现两种情况:
在算一个区间的最大前缀/后缀和时,也会出现两种情况:(以前缀和为例)
至此,我们就可以将线段树build起来了,有一个技巧注意一下,因为在接下来的题里也会用:
重定义node结构体的”+“,或者写一个merge函数,表示node之间的加法,因为不仅有pushup会用到,query也会用。
然后关于query没有什么好讲的吧。就这样,看代码。
1 |
|
没写,这个难,咕
1 |
本题在第一题的基础上加入了单点修改,没有任何攻击性(确信
update就很正常,把边界的点重新赋值,然后重新往上pushup一下就行了。
1 |
|
这题跟前面3问好像没啥关系,不知道为啥放这,但是总体架构仍然不变。
区间求和没有什么难度,但是在修改的时候如果每一个数用sqrt开一下方,常数会剧增,因此需要讨论一种特殊情况:
如果这一个区间的最大值已经<=1了,那么对其开方不会有用,可以跳过
所以在记区间和的基础上记一个区间最大值即可。
1 |
|
在第三题的基础上加入树链剖分。
1 |
|
原创!
这是一件伟大的事情,因为本文的核心诞生于2023年4月12日,距离我开始学习OI仅7个月。
但是当时我还是一名被各种碾压的蒟蒻,于是放弃更新本文,如今,作为略有提升的蒟蒻,我要完善它。
这是对线段树的一个空间优化,因为我当时刚学线段树,还不知道有动态开点这个东西,但是我设想的这个线段树,是完全符合普通线段树的规则,只是略作修改,使其和动态开点线段树一样,变成一棵满二叉树。
哦对了,还有一点很特殊,当时写的线段树都是根为0的,所以左节点是p2+1,右节点是p\2+2。
线段树模板中我们使用的建树方式是将父节点/2下放给子节点的
这样,在设置数组的时候我们总共需要n*4的数组(实际上占了4n-1的空间),这样的建树方式简单明了,容易记忆,同时也广为流传。
但是,如果我们把这棵线段树建成完全二叉树,它的空间需求就可以从n4降到n2(实际为定值2n-1)(如图)
经过计算,我们只需要找到不小于现节点区间大小的最小二的幂num,如果现节点区间大小大于num的3/4,则分界点为l+num/2,否则为r-num/4。
代码如下:
1 | struct |
这种建树方法将空间复杂度减少了一半。
事实上,因为完全二叉树的特性,我们也可以用循环建树,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30struct
{
int sum;
}tree[n<<1];//线段树
void build(ll n)
{
int num=pow(2,__lg(n));
if(n>num)
{
num<<=1;
}
int a=2*n-num;
int b=n;
int i;
for(i=2*n-2;a>=1;a--,i--)
{
tree[i].sum=a;
}
a=2*n-num;
for(;b>a;b--,i--)
{
tree[i].sum=b;
}
for(;i>=0;i--)
{
tree[i].sum=tree[i*2+1].sum+tree[i*2+2].sum;
}
}
线段树的更新为:从上至下分发懒惰标记,在从下至上重新pushup。
这里不再赘述。
Update your browser to view this website correctly.&npsb;Update my browser now