询问最值
1 | int dp[20][maxn]; |
询问最值下标
1 | int dp[20][maxn]; |
1 | int dp[20][maxn]; |
1 | int dp[20][maxn]; |
后缀自动机求母串不包含给定串子串的不同子串数量
首先,需要知道求两个串最长公共子串,即维护第二个串的每个前缀与第一个串的最长公共后缀。
对母串建一个 $sam$,所有串在 $sam$ 上面跑,并维护每个 $sam$ 上每个状态与所有的最长公共后缀长度,因此每个状态对答案的贡献为 $len(s) - dep(s)$。
可以理解原来状态的对子串数量的贡献为 $len(s) - len(link(s))$,$s$ 代表的子串长度区间中每个后缀都对答案有贡献,现在我们将这个区间拆成两块更新答案。
具体来讲,在获取所有点的 $dep$ 值后,基数排序获得 $sam$ 的状态的逆序拓扑排序。
因为需要对于每个状态更新他父亲节点的 $dep$ 值,所以遍历逆序拓扑排序。
这里需要同时从自动机和后缀树两个角度同时思考,当前节点的父亲也是当前节点的后缀,但是计算 $dep$ 时是自动机视角,所以当前节点和他的父亲计算的 $dep$ 没有影响,但实际上当前节点 $dep$ 值对他的父亲是有影响的,例如当前 $dep$ 超过了父亲节点的 $len$,父亲节点对答案是没有贡献的。
1 | namespace sam { |
1 | namespace gsam { |
排序得到左右边界,即为原编号的左右边界,减去序列的长度即可。
求满足 $x_0 \le a, y_0 \le b$ 且 $\frac{x_0}{y_0}=\frac{x}{y}$ 的 $(x_0, y_0)$ 方案数,将 $\frac{x}{y}$ 约分, 答案为 $max(\frac{a}{x}, \frac{b}{y})$。
自闭题T^T。
维护一个 set 存放所有咖啡时间点和在原序列中的下标,每次选 set 的第一个元素 $x$ 放在某一天的第一个,之后二分第一个大于等于 $x+d+1$ 的元素放在同一天并将他从 set 中删除,直到没有元素可以放在这一天。
注意到这样一个结论:如果我们从 $x+1$ 处下落,当 $x+1$ 位于上升气流外,假如 $x+1$ 处下落经过的气流和 $x$ 处经过的气流相同,那么他们滑翔的距离也相同,但是对于x轴的一个位置,$x+1$ 下落总比 $x$ 下落高一个单位,因此有可能经过 $x$ 未经过的气流,所以 $ans(x+1) \ge ans(x)$ ;同样的,如果 $x+1$ 位于上升气流内,$ans(x+1) \le ans(x)$。因此可以得出,最长距离一定会在某个上升气流的左端点出取到。
考虑枚举每个左端点的答案,如果直接计算显然这是 $O(n^2)$ 。
我们还发现,高度下降只会发生在上升气流之间,落地前必定高度下降 $h$ ,在上升气流之间水平移动 $h$ ,加上在经过的所有上升气流的长度和。因此,预处理上升气流长度的后缀和,上升气流之间距离的后缀和。
假设枚举到第 $i$ 个位置,如果当前高度大于当前间隔后缀和,那么表示可以滑翔到最后一个上升气流之后,那么答案就是 $len(i) + h$;如果当前高度小于等于当前间隔后缀和,我们需要找到从 $i$ 开始多少个连续间隔距离加起来大于等于 $h$,也就是后缀和小于等于 $h-delta(i)$,因为是后缀和所以满足单调性,二分即可。
时间复杂度 $O(n+n\log n)$。
1 | struct Edge { |
将n枚硬币分成几块,要求能够每次拿出几块组成任意1到n的整数,块数最小。
考虑多重背包的二进制优化,计算n的二进制位数即可。
给一个序列,每次对一个数加1或减1,求使得中位数等于x的最小操作数。
将原序列排序,二分找到第一个大于x的位置,如果在中位数后则这一部分全部加成中位数,反之亦然。
给两个01串,对第一个串操作,交换两个位置,花费是下标之差的绝对值,翻转一个位置,花费是1。
显然除非两个错误位置时相邻的不同数,否则都使用翻转操作。
将原序列处理成一个012序列,0表示位置正确,12表示错误,1表示错位的是0,2表示错位的是1。
第一遍扫一下这个序列,把相邻的12取出来,第二遍扫一下这个序列加上剩余的12个数。
给一棵树,判断给的序列是否为合法的BFS序列。
合法BFS序列第一个一定是1,然后直接遍历给定的序列。
记录一下每次遍历的一个点时,他的子节点的位置应该在哪。
使用set对比真正的子节点和序列中的子节点。
更新子节点位置,也就是加上当前点的子节点个数。
n个人,m天,每天加一条边,询问一次,当天加边之后,在图中选取一个子图,这些点度数都大于k,输出这个子图的最大大小。
先考虑一个静态的问题,m条边全部给定,那么考虑迭代,直到图上剩余点度数都大于等于k,每次迭代将所有度数小于k的点删除,连接他的边也删除。
优化一下迭代过程,考虑维护一个队列,记录所有度数小于k的点,遍历这个队列,每次遍历到一个点时进行删点和删边,如果使得一个点度数恰好小于k就入队(防止重复入队)。
回到原题,可以反过来处理,变加边为删边,建好整个图之后,每次当天加的边没有删除,就把他删除。
算法复杂度来源于第一次遍历原图。
原题:codeforces867E
不考虑每天实际是否持有股票。
使用一个最小堆,维护每天可能会买进的股票价格,扫一遍整个序列,如果当前的股票价格大于堆顶的价格,那么我们可以买入这个股票,相当于更新最终答案ans,弹出堆顶。
再将新的价格入两次堆,第一次入堆代表卖出的是当前可以买入的,如果后面在购买这支股票,考虑股价之间的差值,使当前股票变为一个中转站,并不会影响最终答案。第二次入堆代表的是购买当前的股票,前一次入堆实际上只是一次中转,并没有实际在此处购买。
两次入堆之间不会相互影响,如果当前入堆的两个后来被卖出了,那么第一次相当于在这个位置做了中转,第二次又买了这个位置则是忽略了那次中转进行再次卖出。
对于记录操作的次数,对于每次买进操作都会对应一个卖出操作,为节点增加一个维度,表示是否是中转站,对于每次真实的买进都会更新操作数量。
1 | #include <cstdio> |
HDu6435 CSGO
1 | #include <cstdio> |
维护一串线段树 + 线段树合并。
首先,注意到小于1e5的整数最多只有100多个因子。
那么我们先预处理小于1e5的所有整数的因子。
对于每个输入的权值,将他的所有因子插入到第i颗线段树上,线段树本身里面没有维护任何值,只是维护了左右儿子的存在性。
之后,我们从后往前扫描整个数组,因为从后面开始的由于输入格式一定是叶子结点,对于每个节点将以他为根的线段树合并到父亲节点上。
合并时,将当前父节点的答案更新为最大值。
合并的意义在于,以他为lca的节点的所有因子合并在一起,并找到最大相同因子。