线段树补充的题目
【CF620E】New Year Tree
给定一棵树,根节点是1
1.将一棵子树的颜色都染为c
2.询问一棵子树内的颜色个数
颜色种类<=60
n,m<=400000
子树相当于一段连续区间,处理出\(dfs\)序之后变为了区间操作问题
考虑颜色范围非常小,可以想到状压
那么,把所有的颜色全部状压好后
操作变成将一段区间全部赋值,询问一段区间的所有数或起来的答案中二进制位上\(1\)的个数
时间复杂度\(O(n+(60+logn)m)\)
【CF914D】Bash and a Tough Math Puzzle
给定一个数列,维护两种操作
1.单点修改
2.在[l,r]区间中,是否可以在最多只修改某一个数的值的情况下,使得gcd为x
利用线段树维护区间的\(gcd\)
每次判断时,计算区间内有几个数的值不是\(x\)的倍数
如果已经大于\(2\)了就不要再做任何计算了,直接返回
【CF438D】The Child and Sequence
给定数列
三种操作
1.区间和
2.单点修改
3.区间取膜
维护区间最大值,如果最大值小于膜数直接返回
否则暴力取膜
不难证明每次取膜当前数至少除二
【BZOJ4491】我也不知道名字是什么
给定一个序列A[i],每次询问l,r,求[l,r]内最长子串,使得该子串为不上升子串或不下降子串
这道题目我懒得写了
口胡一下
对于线段树维护的区间维护以下东西:
区间左(右)端开始(结束)的最长(短)子串的长度
左端右端的值,以及当前区间内的答案
每次向上合并只需要分类讨论即可
然而最后我还是写了
【BZOJ4999】This Problem Is Too Simple!
给您一颗树,每个节点有个初始值。
现在支持以下两种操作:
1. C i x(0<=x<2^31) 表示将i节点的值改为x。
2. Q i j x(0<=x<2^31) 表示询问i节点到j节点的路径上有多少个值为x的节点。
对于每个值,维护一棵线段树就好啦
动态开点,否则空间开不下
剩下的就是很简单的问题啦
当然了,对于数值要离散化
没必要离线吧,在线用\(map\)维护就行了
【BZOJ1858】序列操作
给你一个01序列
5个操作
1.区间修改为0
2.区间修改为1
3.区间01翻转
4.询问区间1的个数
5.询问区间最大的连续1的个数
这题思路很简单,细节很烦,很码
维护区间翻转和区间赋值标记
当打到区间赋值标记时直接覆盖掉翻转标记
下放标记的时候先放赋值标记再放翻转标记
这样可以维护前4个操作
对于第5个操作
维护区间从左/右端点开始的最大连续\(0/1\)的个数
以及区间内的最大连续\(0/1\)的个数
做区间翻转的时候所有的关于\(0/1\)连续个数的计数全部要交换过来
其他的细节自己注意一下
【BZOJ1835】基站选址
这个直接放题面算了,不好简化
有N个村庄坐落在一条直线上,
第i(i>1)个村庄距离第1个村庄的距离为Di。
需要在这些村庄中建立不超过K个通讯基站,
在第i个村庄建立基站的费用为Ci。
如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,
那么就村庄被基站覆盖了。
如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。
现在的问题是,选择基站的位置,使得总费用最小。
考虑一个比较暴力的\(dp\)
设\(f[i][j]\)表示建了\(i\)个基站,最后一个的位置是\(j\)的最小代价
考虑如何转移\(f[i][j]=min(f[i-1][p]+Cost(p+1,j)+C[j])\)
其中\(Cost\)表示代价,也就是区间内所有没有被覆盖的村庄的\(W\)的和
如果直接暴力\(dp\),复杂度\(O(n^2k)\),这个复杂度还假设了\(Cost\)是\(O(1)\)计算的
转移的时候是枚举建造的个数,显然还可以滚调第一维
但是这个复杂度我们无法接受,我们要思考有没有更快的方法
考虑\(dp\)的转移式子
\(f[i][j]=min(f[i-1][p]+Cost(p+1,j)+C[j])\)
\(f[i][j]=min(f[i-1][p]+Cost(p+1,j))+C[j]\)
相当于我们要找到的就是\(min(f[i-1][p]+Cost(p+1,j))\)
我们不难知道,如果一个村庄不需要贡献他的\(W\)
那么,必须在一个区间内存在一个基站
所以,这个区间我们可以提前预处理出来(二分一下)
然后按照右端点排序,按照右端点结束的位置分类
从左到右这样扫过来,如果过了某个右端点
此时,这些区间的最右端在当前位置的那些村落,
在后面的\(dp\)中必然产生贡献,
会对\([1,left-1]\)的所有区间都产生\(W\)的贡献
因此在线段树上做一次区间加法
每次进行转移的时候从所有的位置中选出区间最小值就行了
然后\(K\)增大,重构线段树即可
【BZOJ2962】序列操作
给你一个数列,三个操作
1.区间加法
2.区间取相反数
3.询问从区间中任选k个数的乘积的和是多少?
n<=50000,k<=20
设\(s[i]\)表示区间内选择\(i\)个数的乘积的和
考虑如何向上合并?
\(s[k]=\sum_{i=0}^klson.s[i]*rson.s[k-i]\)
相当于是一个卷积形式
区间取相反数是一个很好处理的操作
把所有的\(s[k],k\&1=1\)取相反数就好了
区间加法?
假设我们已经知道了原来的所有的答案
现在的数从原来的\(a[1],a[2],....\)
变成了\(a[1]+x,a[2]+x,....\)
把乘积的形式拆开
发现变成了组合数乘\(x\)的若干次幂再乘上原来\(s[i]\)的值得形式
直接修改即可
【CJOJ2535】BRS
给定一个数列
1.求一段区间内的最大子段和
2.单点修改
对于当前区间,维护最大左段和,最大右段和,最大答案,区间和
至于怎么合并自己思考吧。。。。
【CJOJ2553】snow的追寻
给定一棵树
每次询问除了两棵子树以外,
剩下的节点构成的树的直径
线段树维护树的直径
我们知道,子树在\(dfs\)序上对应着连续的一段
所以,可以用线段树维护\(dfs\)序连续一段的子树
那么,维护这一段的直径的两个端点
考虑如何向上合并
我们知道,到树上一个的最远点是直径的端点
所以,两棵树合并在一起的直径一定是两棵子树的直径的四个端点两两配对的方案中
最长的那两个点
这样子就可以向上合并了