真,顶级毒瘤题目,浪费我至少一天。
首先不难想到对于修改,有一个暴力序列线段树做法:
-
如果当前区间的最大值 \(\le x\),那么直接返回,无法进行修改。
-
如果当前区间的最小值 \(\ge x\),那么区间减,打上懒标记即可。
-
否则,就暴力修改左右儿子然后 \(\texttt{pushup}\)。
显然,由于正常人都会造数据,使得每次修改都被无限细分到叶子节点,所以必然会被卡。
我们发现,这里的瓶颈在于我们不能确定到底该如何修改。
考虑对权值进行分块,每个块维护一颗序列线段树。
但是这里权值太大,就算分出来,也完全无法构造线段树。
所以考虑倍增分块(当然,这只是这样分块的其中一个原因)。
我们定义一个进制 \(B\),将 \([B^i,B^{i+1})\) 分成一块, 然后,对于每块如上建立线段树。
修改的时候,对于块 \(i\)(假设其左右端点分别为 \(l_i,r_i\)):
-
\(l_i\ge x\),显然,这整个块根本就无法修改。
-
\(r_i< x\),这个稍微处理一下就好。显然这个块内所有下标在 \([l,r]\) 以内的数都要修改,这个可以直接通过懒标记解决。但是考虑到你减了之后可能会改变你所在的块,所以我们对于那些整个区间减了之后都还在这个块内的打上懒标记,其余的就暴力修改,然后暴力变块。
-
\(l_i<x\le r_i\),即有些修改有些不改。这个就同我们最开始的暴力线段树修改,不在阐述,注意减了之后变块的问题就行。
然后我们来考虑一下时间复杂度的问题。
首先是变块,由于我们精准的找到了要变的位置,加之还有插入线段树,所以变块只会产生 \(\mathcal{O}(n\log_BV\log_2n)\) 的时间损耗,问题不大。
但是一些线段树内部递归消耗还值得我们去思考,由于作者暂时还没有搞得很清楚,所以先咕咕咕。
当然,这样还没有结束,你会发现你光荣 \(\texttt{MLE}\) 了。
仔细思考可以发现,你线段树是4倍空间,原因是你分的太细了,要一直分到叶子节点。
那我不分这么细就行了嘛。
所以考虑另外一个技巧:底层分块。其实有点像阈值分治,就是如果你在线段树上递归到的当前区间长度小于一个阈值 \(K\),那就把这个区间当作叶子节点暴力枚举处理。
当然,这样写的一个非常重要的细节是,你每次暴力进入区间计算时,记得把这个点的标记暴力传到这个区间上。
对于具体参数,\(K=32,B=16\),即可。
标签:暴力,分块,线段,Ynoi2007,修改,区间,rgxsxrs,变块 From: https://www.cnblogs.com/SFsaltyfish/p/18077231