2019-9-14做题记录
1、【BZOJ1493】【NOI2007】项链工厂
已经是十二年之前的$NOI$了,现在看来古人们学的东西我都不会。
一个环,每个元素有颜色,六种操作。
- $Rotate\ k$:将项链顺时针旋转$k$个单位。即原$i$位置变成现在$i+k$位置。
- $Flip$:将项链沿指定对称轴翻转,即将项链的$i$位置和$n+2-i$位置互换。
- $Swap\ i\ j$:将项链的$i$位置和$j$位置的颜色互换。
- $Paint\ i\ j\ x$:将项链从$i$位置开始,顺时针$j$个长度的区间染成$x$。
- $Count$:询问整个项链上颜色块数。
- $CountSegment\ i\ j$:询问项链从$i$位置开始,顺时针$j$个长度的区间颜色块数。
这个东西肯定是用$splay$更好维护,但是我非要用线段树。
首先,我们把环变成一条链,$5,6$两个操作显然可以在线段树上直接套用维护颜色块数的方法,当越过终点是再把两个答案合并起来就行了。
$3,4$也很简单,单点修改和区间赋值。
$2$也很简单,向$splay$那样打翻转标记即可,等到查询的时候一条链$pushr$下来。
$1$只是映射关系的改变,我们记录一下,等到查的时候还原回来就可以了。
看了题解,发现$2$假了,因为线段树是不能向平衡树那样随意分离出子树(子区间的),所以维护映射关系的时候乘个负号就行了。
2、【CF666E】Forensic Examination
给你一个串$S$以及一个字符串数组$T[1..m]$,$q$次询问,每次问$S$的子串$S[pl..pr]$在$T[l..r]$中的哪个串里的出现次数最多,并输出出现次数。
如有多解输出最靠前的那一个。
这不是我多年之前跳过的后缀自动机码农题吗?估计现在早已忘了,没记错的话,应该是后缀自动机三连。所以就不写了。
3、【CF700E】Cool Slogans
给出一个长度为n的字符串$s[1]$,由小写字母组成。定义一个字符串序列$s[1..k]$,满足性质:$s[i]$在$s[i-1] (i \ge 2)$中出现至少两次(位置可重叠),问最大的$k$是多少,使得从$s[1]$开始到$s[k]$都满足这样一个性质。
$s[i]$在$s[i-1]$中至少出现两次,那我们可以把$s[i-1]$的后面去掉没有被覆盖的部分,这样的话,就是在后缀树上对应着祖先/后代关系的一条链,直接线段树合并判断能否接上并且求个$dis$就行了?
4、【BZOJ3672】【NOI2014】购票(线段树,斜率优化,动态规划)
众所周知,这是道神仙题。
所有的城市形成一个树的结构,从城市$v$前往$SZ$市的方法为:选择城市$v$的一个祖先$a$,支付购票的费用,乘坐交通工具到达$a$。再选择城市$a$的一个祖先$b$,支付费用并到达$b$。以此类推,直至到达$SZ$市。
对于任意一个城市$v$,我们会给出一个交通工具的距离限制$lv$。对于城市$v$的祖先$a$,只有当它们之间所有道路的总长度不超过$lv$时,从城市$v$才可以通过一次购票到达城市$a$,否则不能通过一次购票到达。对于每个城市$v$,我们还会给出两个非负整数$pv,qv$作为票价参数。若城市$v$到城市$a$所有道路的总长度为$d$,那么从城市$v$到城市$a$购买的票价为$dpv+qv$。
求每个城市到根的最小花费。
这里给出三个$log$的线段树分治做法(显然,是跑不满的)
是类似“火星商店问题”的方法,即单点修改和区间查询。在这里,区间即树链。
树链剖分一次,用线段树维护$dfs$序,线段树的每个节点维护一个$vector$,记录凸包信息。
每次查询确定$logn$个重链段,再分别对这$logn$个重链段的每一个确定$log$个线段树上的区间,然后在区间上二分查找即可。
每次修改把沿向上的路到的节点的$vector$中插入这个点,并且检查周围被它变得不凸的点。
算法的瓶颈在于查询,三个$log$,本来我试图“对树链剖分进行阉割”把它修改成两个$log$的,但是由于树链剖分优美的结构,未能实现。
也想过用$LCT$代替树链剖分,但是$splay$不能快速合并两个凸包,也未能实现。
网上有两个$log$的树分治作法,可能并不是很难,然而我并不会。。。。。。。。
这个思路得以实现的原因在于凸包的维护是可以快速插入一个新点的(就像$trie$树一样),并且耗得空间不是很多(一个新点多$logn$的区间),而且要求在线。
5、【LR#6】花火(线段树、扫描线)
$n$烟火排成一排,从左到右高度分别为$h_1,h_2,...h_n$,这些高度两两不同。
- 每次$Yoko$可以选择两个相邻的烟火交换,这样的交换可以进行任意多次。
- 每次$Yoko$还可以选择两个不相邻的烟火交换,但这样的交换至多进行一次。
你的任务是帮助$Yoko$用最少次数的交换,使这些烟火从左到右的高度递增。
要是我们可以直接得到一个序列选择两个相邻的烟火交换最少需要多少次就好了,这样我们直接枚举交换哪两个,然后$O(1)$算,就可以得到了$O(n^2)$的做法了。
上面这个问题的答案是排列中的逆序对数(见【NOIP2015】火柴排序),逆序对直接用数据结构维护。
但是这样还不是很优秀,所以我们再考虑怎么交换才是有意义的,对于$(x<y)$,如果它们要互换,显然有$h[x]>h[y]$(因为只有这样换了才会减少逆序对数)
进一步,$x$必须是前缀最大值,并且$y$必须是后缀最小值。这个东西我们可以用$lrj$优势人群的方法理解,每个人有它控制的区间和它的权值,从这两个维度上考虑必须是极大的。
所以就用扫描线二维数点就完了。
6、【BZOJ3730】震波(动态点分治)
动态点分治(点分树)的题。
一棵树,两种操作。
- 询问距离$x$不超过$k$的所有节点的权值之和。
- 修改某一个节点的权值。
今天($2019.9.16$)把这道题给$aysn$看,被$aysn$秒切了,说这是套路题,然而我什么都不会。
如果说“点分树”是线段树,那这道题就是“线段树套平衡树”了,$hh$。
每一个分治中心维护一个$BST$(树状数组亦可),记录着点到重心的距离和点的权值组成的二元组,每次把有贡献的点限制在一个区间里,查区间和即可。
老生常谈的,这样儿子的信息会被父亲又算一遍,所以我们对于儿子再开一个$BST$,存儿子在父亲那里的信息,查父亲的时候把这个信息减去就可以了,所以只要两个指针一父一子一起往上爬即可。
7、【BZOJ4311】向量(线段树分治)
一个向量集,三种操作。
-
插入一个向量$(x,y)$
-
删除插入的第$i$个向量
-
查询当前集合与$(x,y)$点积的最大值是多少。如果当前是空集输出$0$
已经是老生常谈的“插入-删除”形式了,一个向量在线段树上存在的时间形成一个区间,然后把它加到对应的$log$个节点上去。
查询的时候一条链查下来,复杂度是两个$log$的。
8、【BZOJ3064】CPU监控(线段树)
一个序列,四个操作。
- 区间加
- 区间赋值
- 区间最大
- 区间历史最大
吉老师线段树的神仙题。
如果我们直接把平时用的线段树套到上面,并且加一个域表示这个区间的历史最大值,然后每次把这个域和区间当前最大值取$max$,行不行呢?
这显然是不行的,比如我在上面某个节点加了一个正数,再减一个正数,问底下的历史最大值,在这个过程中,标记就根本没有碰到底下的节点,所以就假了。
所以我们要记录一下加法和取$max$看做一个操作,用两个域维护懒标记,就像加和乘一样推合并标记的式子。
9、【BZOJ2054】疯狂的馒头(并查集,线段树)
一个序列,两个操作:
- 区间染色
- 最后询问每个点最后被染的颜色
这道题显然是一个线段树区间赋值维护序列,但是这样可能会$T$,所以我们想想有没有其他的方法。
从后往前做,暴力染色,用并查集快速找到下一个未被染色的地方(即让代表元代表自己和左侧已经被染色的点的集合),染色后直接把左合并到右上即可。
10、【BZOJ5286】[HNOI2018]转盘(线段树)
一个转盘上有摆成一圈的$n$个物品(编号$1~n$),其中的$i$个物品会在$T_i$时刻出现。
在$0$时刻时,小$G$可以任选$n$个物品中的一个,我们将其编号为$s_0$。并且如果$i$时刻选择了物品$s_i$,那么$i+1$时刻可以继续选择当前物品或选择下一个物品。当$s_i$为$n$时,下一个物品为物品$1$,否则为物品$s_{i}+1$。在每一时刻(包括$0$时刻),如果小$G$选择的物品已经出现了,那么小$G$将会标记它。小$H$想知道,在物品选择的最优策略下,小$G$什么时候能标记所有物品?
但麻烦的是,物品的出现时间会不时修改。我们将其描述为$m$次修改,每次修改将改变其中一个物品的出现时间。每次修改后,你也需求出当前局面的答案。对于其中部分测试点,小H还追加了强制在线的要求。
这道题我之前已经做过了,就是正难则反,然后发现一定要走一圈,而且不能停,因为一旦停就消失。
然后破环为链,对出现的时间减去位置,维护单调栈(楼房重建)
11、【BZOJ2212】[POI2011]Tree Rotations (线段树合并)
给一棵$n(1≤n≤200000)$个叶子的二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少。
$aysn$神仙题。$orz$就是了。
考虑二叉树的两个节点间的贡献,用分治的思想,我们知道,总的逆序对是左子树的逆序对+右子树的逆序对+左右之间的贡献。
从下到上考虑,我们要决定的就是把哪个子树放到左边,哪个子树放到右边。
我们要算的东西就是对于左子树的每个数,有多少个右子树的数比它小,而这个东西我们恰好可以用权值线段树维护。
在合并的过程中算两个值,然后选一个小的加上去,作为最终的值,就可以了。
12、【BZOJ4821】[SDOI2017]相关分析(线段树)
现在$Frank$要分析参数$X$与$Y$之间的关系。他有$n$组观测数据,第$i$组观测数据记录了$x_i$和$y_i$。他需要一下几种操作
- $1\ L,R$
用直线拟合第$L$组到底$R$组观测数据。用$\overline{x}$表示这些观测数据中$x$的平均数,用$\overline{y}$表示这些观测数据中$y$的平均数,即
$\overline{x}={1 \over R-L+1} \sum _{i=L} ^R x_i$
$\overline{y}={1 \over R-L+1} \sum _{i=L} ^R y_i$
如果直线方程是$y=ax+b$,那么$a,b$应当这样计算:$$a={\sum_{i=L} ^{R} {(x_i-\overline{x})(y_i-\overline{y})} \over {\sum _{i=L} ^{R} {(x_i -\overline{x})^2}}}$$
你需要帮助$Frank$计算$a$。
- $2\ L,R,S,T$
测量数据第$L$组到第$R$组数据有误差,对每个$i$满足$L≤i≤R$,$x_i$需要加上$S$,$y_i$需要加上$T$。
- $3\ L,R,S,T$
对于每个$i$$(L≤i≤R)$,$x_i$需要修改为$(S+i)$,$y_i$需要修改为$(T+i)$
这个应该是一个很显然的线段树对序列信息的维护吧。
维护$\sum{x},\sum{y},\sum{xy},\sum{x^2}$即可。
13、UOJ#46. 【清华集训2014】玄学
巨酱有$n$副耳机,他把它们摆成了一列,并且由$1$到$n$依次编号。每个耳机有一个玄学值,反映了各自的一些不可名状的独特性能。玄学值都是$0$到$m-1$间的整数,这些耳机的玄学值会发生改变。特别地,巨酱观察发现,每种作用$o$对应了两个整数$a_o$与$b_o$,在这种作用之后,玄学值原本为$x$的耳机,其玄学值恰会变成$(a_ox+b_o)mod\ m$。
为了尽快完成方案的制订,巨酱希望自己能高效地完成以下工作:
- 巨酱想到了一种操作,能让耳机的玄学值由$x$变为$(ax+b)mod\ m$,并且他计划对编号为$i$到$j$的耳机执行这种操作。
- 巨酱想知道如果将(并且仅将)自己的第$i$个到第$j$个计划按顺序付诸行动,编号为$k$的耳机的玄学值将会变成多少。
如果没有问$i$,$j$,我们就可以直接线段树维护区间乘加,但是它限制了修改的范围。
或者用线段树分治,对操作(变换)开线段树,每个问题对应一个区间,然后求每个节点的变换,从下到上合并或者从上到下暴力。算的时候直接对那些区间都做一次然后合并起来就可以了。
在线时,我们当然也可以用线段树分治的方法。$yyb$说可以二进制分组,那我就看一下吧。
14、【LOJ#6029】市场(线段树)
一个序列,每个元素有权值$a_i$。
- $1\ l\ r\ c$,对于$i \in [l,r]\ a_i\gets a_i+c$
- $2\ l\ r\ d$,对于$i \in [l,r]\ a_i\gets \lfloor \frac{a_i}{d} \rfloor$
- $3\ l\ r$,求$min_{i \in [l,r]}a_i$
- $4\ l\ r$,求$\sum_{i \in [l,r]}a_i$
势能分析线段树。
之前还做过一道开根的,总体而言是一样的。
应该把除法变成减法。如果区间内最大值和最小值整除操作之后的差相等,那么可以变为区间减法。否则暴力下放。