图片排版
题目来源
题目大意
有(n)张图片,第(i)张图片大小为(A_i imes B_i)。
要把这些图片按顺序放到一个宽度为(m)的文档里,原则如下:
- 如果这一行剩余宽度为0,则跳至下一行;
- 如果这一行剩余宽度大于等于该图片的宽度,则放置;
- 否则把该图片长宽等比缩小,使得宽度刚好可以放下,高度向上取整;
- 整个文档会排列成若干行,每一行的高度为最高的图片的高度。
你可以删去其中某一张图片,使得剩余图片按原顺序重新排版,总高度最小。
范围:(n le 100000),(m le 100)。
算法介绍
令(F(i))表示前(i)张图片放置的结果,需要记录行数、剩余宽度、当前行高度。
令(G(i,j))表示最后(i)张图片,从剩余宽度(j)开始放置的结果,需要记录行数、当前行高度。
删除一张图片,只需要对应的合并(F)和(G)即可。
可以用动态规划得到(F,G),效率为(O(nm))。
震荡数组
题目来源
题目大意
对于一个长度为(n)的数组(A),若对于所有(i in (1, N))都满足,(A[i]> max(A[i-1],A[i+1]))或者(A[i]< min(A[i-1],A[i+1])),则称该数组为震荡数组。
现在给出数组(A),你可以对它进行若干次操作,每次操作可以交换一对数。请问最少进行多少次操作,可以把(A)数组变成震荡数组。
范围:(n le 29),(A_i)两两不同。
算法介绍
容易发现,答案的上界是(frac{n}{4});所以直接搜索就可以啦!
当然需要加入一些剪枝。
效率:(O( ext{玄学}))。
子矩阵求和
题目来源
题目大意
有一个无限大的矩阵(A),(A_{i,j}= min(i,j))。
给出(n,m,P),找出矩阵(A)中一个(n imes m)的子矩阵,满足元素和是(P)的倍数。要求该矩阵的左上角坐标尽量小。
范围:(n,m le 10^5),(P le 10^6)。
算法介绍
首先,若给出(A)中的一个子矩阵((x0,y0,x1,y1)),是可以(O(1))计算和的;容斥一下即可。
观察发现,有(A_{i+1,j+1}= A_{i,j} + 1),所以当一个(n imes m)的子矩阵整体向右下方移动(t)格时,元素和就增加了(tnm mod{p})。
所以只需要枚举第一行(列)的位置(x),先算出左上角((1,x))的矩阵和,然后用拓展欧几里得就可以得到整体向下移动的格数。
容易发现,左上角为((1,n))和((1,n+1))以及((1,n+2),...)的矩阵和相同,所以不需要继续枚举(x)了。
因为本题(P)较小,可以先(O(P))预处理,这就不要拓欧了。
效率为(O((n+m) log P))或(O(n+m+P))。
神奇的子串
题目来源
题目大意
对于一个长度为(n)的01串(S),它的权值定义为,它所有长度在([n-K+1,n])之间的子串中,本质不同的子串个数。
给出(n,K),要求对所有可能的串(S)(一共(2^n)种),计算权值和。
范围:(n le 10^8),(K le 15)。
算法介绍
如果想让多个相同的项只贡献1,通常的方法有:
- 最小表示法(或称“唯一表示法”),即让多个相同项只在某个代表元素上被计算到一次。
- 容斥,比如二项式反演。
本题可以用容斥来解决。
首先枚举(t in [1, K]),则需要计算串(S)的(t)个子串中本质不同的数量。可以(2^t)枚举哪些串是相同的,接着用并查集得到哪些位置的元素是相同,若最后串(S)有(x)个不同的集合,则贡献为(2^x)。
串长很大,朴素的并查集合并效率爆炸。容易发现,串(S)是呈类似“循环串”的形式,故只需要保留最后(2t)个位置即可。
效率为(O(2^K K^3))。
Rikka with Tree III
题目来源
题目大意
给一棵(n)个节点的树,每个点有点权(w_i)。
若三元组((x,y,z)),点(y)在点(x)与点(z)的最短路径上,(w_x,w_y,w_z)呈等差数列,则称(w_y-w_x)(即公差)是合法的。
求有多少种不同的合法公差。
范围:(1 le w_i le n le 10^5)。
算法介绍
算法一
点(y)为点(x)和点(z)的LCA
这是一个经典的问题,直接启发式合并即可,效率(O(n log^2 n))。
点(x)在点(y)的子树内,点(z)在点(y)的子树外
显然只需要得到点(y)子树内所有点权值的bitset,以及子树外所有点权值的bitset,取交即可。
然而因为内存的限制,无法用DFS得到bitset,这让我们很苦恼!
可以用DFS序,把子树内和子树外转化为了区间信息;然后用莫队就可以了!
效率为(O(n sqrt{n} + frac{n^2}{32}))。
算法二
将树轻重链剖分后,每个点向上最多会跳(O(log n))条不同的重链;这也意味着,如果对于每个点,都将它所有轻儿子的子树DFS都遍历一遍,总效率为(O(n log n))。利用这个性质,我们来分别优化算法一中的两个计数部分。
点(y)为点(x)和点(z)的LCA
首先我们得到了点(y)子树内点的权值信息(每种权值有几个,不止是bitset)。先把所有轻儿子子树都删去,然后再逐一将轻儿子子树加入,加入时顺便得到合法公差。
得到点(y)重儿子的子树权值信息,只需要将轻儿子删光即可;得到轻儿子子树权值信息,只需要暴力DFS加入即可。
效率为(O(n log n))。
点(x)在点(y)的子树内,点(z)在点(y)的子树外
首先我们得到了点(y)子树内的权值信息,以及子树外的权值信息;同时还知道全局的权值信息。
重儿子 只需要暴力将所有轻儿子子树内的点转移,就能得到重儿子子树内和子树外的信息。
轻儿子 直接在全局的权值信息中,把轻儿子子树的点转移,就能得到轻儿子子树内和子树外的信息。
注意,我们需要对每条重链分别做。链头(必然是父亲的轻儿子)的子树信息直接从全局处得到,之后只DFS重儿子;重儿子信息由删去轻儿子子树得到。每条重链做完后需要还原全局信息
因为本题的bitset必不可少,所以不太能显示出算法二的强势......
时间复杂度(O(n log n + frac{n^2}{32})),空间复杂度(O(n))。
Rikka with String
题目来源
题目大意
给一个长度为(n)的字符串(S),需要对于每个(i(0<i<n))求出:假设将串(S)在第(i)位和第(i+1)位间裂开成两个串(S_1)和(S_2),有多少不同的串是(S_1)或(S_2)的子串。
范围:(n le 200000)。
算法介绍
处理字符串的子串问题,通常采用SAM。SAM中的每个节点(p)就表示了以(right_p)集合为右端点,长度在((Max_{fa_p},Max_p])之间的一坨子串;每个节点表示的子串都是互不相同的。
通常并集比较难求,于是考虑补集转化后求解交集。即用 (串(S)中的子串个数 - 既不是(S_1)也不是(S_2)的子串个数) 得到答案。
假设某个子串在串(S)中的出现位置为([l_1,r_1],[l_2, r_2],...,[l_k,r_k]),考虑该串会对哪些(i)产生贡献(即没有在(S_1)和(S_2)中出现),只需要满足(i)选在([l_1,r_1],[l_2, r_2],...,[l_k,r_k])的交上即可,即对所有(i in [max(l_1,l_2,..,l_k), min(r_1,r_2,...,r_k)])产生贡献。
对于串(S)的SAM中每个节点(p)代表的串统一考虑,只需要特判几种情况,对(len in (Max_{fa_p},Max_p])的交进行区间加一;可以转化为区间加公差为1的等差数列。
效率为(O(n))。
拓展
如果题目变成求“有多少不同的串是(S_1)和(S_2)的子串”,方法类似。
Rikka with Grid
题目来源
题目大意
给出一个(n imes n)的01矩阵。进行(m)次询问,要求有多少大小为(a imes b)的,四条边界全为1的子矩阵。
范围:(n,m le 1500)。
算法介绍
朴素的想法,令(A[i][j][k])表示第(i)行,向下至少有(j)个连续全1的格子,第(k)列是否合法;(B[i][j][k])表示第(i)行,向左至少有(j)个连续全1的格子,第(k)列是否合法。
(A,B)的最后一维可以用bitset表示,且(A[i][j],B[i][j])可以由(A[i][j-1],B[i][j-1])得到,效率为(O(frac{n^3}{32}))。
每次询问,先枚举上边界是(i)行,通过(A[i][b],A[i+a-1][b],B[i][a])的bitset移位取交,就可以计算答案啦!时间和空间复杂度均为(O(frac{n^3}{32}))。
可以用st表优化内存。以(A)为例,预处理时得到(A[i][2^0],A[i][2^1],...,A[i][2^k])的信息;对于任意的(j),(A[i][j])可以由(A[i][2^t])和(A[i+j-2^t][2^t])取交得到。空间复杂度为(O(frac{n^2 log n}{32}))。
Little Y's Tree
题目来源
题目大意
给一棵(n)个节点的树,进行(q)次询问。
每次询问给出(K)条边,假设这(K)条边从树上删除,整棵树会划分成(K+1)个连通块,求出每个连通块的直径的和。
范围:(n,q,sum{K} le 10^5)。
算法介绍
求树上一个点集的直径
这是一个经典问题。考虑逐个加入点(x),时时维护出点集直径的两个端点((a,b));拿((a,x))和((b,x))去更新直径即可。
如果合并两个点集,只需要拿两个点集各自直径的两个端点(一共4个点),两两组合更新直径即可。
连通块 -> DFS序
容易发现,删去(K)条边后,剩下的(K+1)个连通块都可以表示成一些DFS序的并。所以用线段树维护每个DFS序区间的直径即可。
效率为(O((n+q+sum{K}) log n))。
Automorphism
题目来源
题目大意
对于两棵(n)个节点的无根树(T_1,T_2),它们同构当且仅当,存在一个排列P使得(T_1)中((u,v))有边当且仅当(T_2)中((P(u),P(v)))有边。
一棵树(T)的自同构数量为(T)中((u,v))有边当且仅当(T)中((P(u),P(v)))有边的排列(P)的数量。
请问有多少棵本质不同的(不同构),自同构个数不超过(m)的无根树。
范围:(n le 50),(m le 10^9)。
算法介绍
树的自同构计数
有根树
令(F[u])表示点(u)子树的自同构排列数量,假设子树中有(x)个同构的子树,它们的自同构数都是(y),则贡献为(y^x x!);而(F[u])就等于所有贡献相乘。
无根树
取重心为根,转化为有根树情况。遇到双重心情况,若重心两侧的子树同构,则答案再乘二。
本质不同的树计数
有根树
令(A_i)表示(i)个点本质不同树的数量,(B_i)表示(i)个点本质不同的森林数量。
(A_i= B_{i-1}),再把(A_i)扔进去和(B)背包一下更新(B)。
无根树
默认根是重心,则只允许将(i le frac{n}{2})的(A_i)拿进去背包。
对于双重心的情况,如果重心两侧的子树不同构,则同种树会被重复算到2次,需要减去。
原问题
原问题是两者的结合,考虑用Dp套Dp。
先预处理一个系数(c[i][j][k])表示有(i)个本质不同的树(但是每种树可以选多个),一共选了(j)棵树,(k)为自同构数量的一部分(若某个树重复选了(x)个,则贡献为(x!),(k)为贡献的乘积;只保留不超过(m)的(k),显然状态数很少),(c[i][j][k])表示这种情况下的方案数。
令(A_{i,j},B_{i,j})分别表示(i)个点,自同构数为(j)的树和森林的数量(只保留不超过(m)的(j),显然状态数很少)。同样有(A_{i} = B_{i-1}),再把(A_i)扔进去和(B)背包一下更新(B)。
背包时,需要对于所有(j)枚举(A_{i,j});然后枚举从(A_{i,j})选了(a)个本质不同的树(乘个组合数),一共选了(b)个树(同一种树可以选择多个,贡献乘个(j^b));然后对所有(k),枚举(c[a][b][k]);再枚举所有(B_{x,y})来背包。
因为是无根树,需要令根为重心,即只允许将(i le frac{n}{2})的(A_i)拿进去背包。且最后还需要判断双重心的情况。
复杂度为(O( ext{玄学})),因为有效的Dp状态数非常少,所以能过。
Rikka with Sequence II
题目来源
题目大意
给出(n)个数(A_i),要求选出若干个数,使得平均数小于等于中位数;求方案数。
范围:(n le 40)。
算法介绍
先将序列(A)从小到大排序,然后枚举(i,j)确定中位数(Mid= (A_i + A_j)(2))。将另外数都减去(Mid),对于(t in [1,i))的(A_t),权重为(-1);(t in (i, j)),权重为(0);(t in (j, n]),权重为1。我们就要求出剩下数中有多少子集,权重恰好为0,权值和小于等于(Mid)。
可以用折半搜索求解。值得注意的是,如果先(DFS)出所有集合然后排序,效率会多乘只(n)。考虑用BFS的形式拓展,考虑当前数是否选,将两个数组归并排序即可。
效率为(O(2^{frac{n}{2}} n))。
Rikka with Graph
题目来源
题目大意
给出一张(n)个点(m)条边的无向图。考虑这张图所有边的子集(一共(2^m)个),对于每一个边子集,建出它的导出子图,这个子图由若干联通块构成,要求至多一个联通块是基环树,其他都是树。该方案的权值,为所有连通块的大小的乘积。
对所有情况求权值和。
范围:(n le 16)。
算法介绍
算法一
先(2^n)枚举环,将其缩点;再建立一个辅助点,将该点与图中剩下点以及新缩点连边,求生成树个数即可。效率为(O(2^n n^3))。
需要预处理每个点集(S)构成环的方案数,可以(O(2^n n^2))Dp出来。求生成树个数可以对基尔霍夫矩阵求行列式!
算法二
显然只需要对于所有点集(S),求出(S)构成环的方案数、构成树的方案数、构成基环树的方案数;用个simple的子集Dp合并起来就行了。前两者都非常好求,这里关注如何对于所有点集,求出基环树的方案。
效仿容斥求拓扑图个数的方法,令(F(S))表示点集(S)的基环树方案,枚举该基环树最外面的一层点(即叶子集合),将它“剥掉”后剩下的还是一棵基环树。再用容斥的思想,枚举最外层的点集(T),要求这些点一定是叶子,但是对于(S-T)中的点,也可能是叶子。即:
令(Deg(x,B))表示点(x)在点集(B)中有多少条边,则(E(A,B)=prod_{x in A} Deg(x,B))。
稍加留心会发现,该算法的瓶颈在于计算(E(A,B)),暴力求解的效率为(O(3^n n))。可以把枚举子集转化为枚举超集,即枚举集合(S-T),对所有点(x)预处理(Deg(x,S-T));之后DFS所有可行(T),顺便计算出(E(T,S-T))即可。
效率为(O(3^n))。
欧拉子图
题目来源
题目大意
给一张(n)个点(m)条边的无向图。
考虑它所有边集的子集(一共(2^m)个),对于每一个子集,它构成的图必然是若干个连通块,要求每一个连通块都存在欧拉回路;这种方案的权值即为边集大小的平方。
求所有方案的权值和。
范围:(n le 60),(m le 100)。
算法介绍
方法一
如何判定一张图的每个连通块都存在欧拉回路? 显然只需要每个点度数均为偶数即可。
如何单纯计算方案数呢? 直接高斯消元解异或方程组!最后2的自由元个数次幂就是答案。用个bitset优化,效率为(O(frac{1}{32} n^2 m))。
现在要对平方求和,不妨把每个平方的贡献分别对应到每一对边上。即(O(m^2))枚举两两对边,然后用高斯消元求出包含这两条边的欧拉图方案数,累加即可。
效率:(O(frac{m^3 n^2}{32}))。
方法二 —— K次方
该方法常用于稀疏图上
考虑一个暴力Dp,令(F[i][j][t])表示处理了前(i)条边,(j)是一个(2^n)的状态,记录每个点的奇偶性,(t)的范围为(0..K),表示(t)次方的答案。每次考虑第(i+1)条边是否选,随便转移一下即可。效率为(O(m 2^n K))。
事实上(j)的状态数是远远不到(2^n)的。对于每个点(x),找出它在这(m)条边中第一次出现的位置,以及最后一次出现的位置。显然点(x)只有在这段区间中,状态才可能是0或者1;否则状态一定是0,根本没必要记它。所以对于每个(i),(j)的状态数为2的覆盖点数次幂个。
因为是稀疏图,通过随机重置这(m)条的顺序,可以在很快的时间内找出状态数总和(S)小于等于(10^6)的方案。之后Dp的效率就为(O(SK))。
String Problem III
题目来源
题目大意
给出(n)个字符串(S_i),求两两之间编辑距离在([1,8])之间的对数。
编辑距离为:每次删除、插入或修改一个字符,使得两个串相等的最少操作步数。
范围:(n le 200),(sum |S_i| le 1000000)。
算法介绍
求解两个串的编辑距离 似乎只能Dp吧?令(F[i][j])表示(A)串前(i)个和(B)串的前(j)个的编辑距离;随便转移一下。效率为两者串长的乘积。
这道题显然不能用Dp算,太慢了。注意到一个神奇的限制,距离小于等于8,不妨用搜索。
初始设两个指针(x,y)都为1,求出(x,y)之后串的LCP,得到第一个不同的位置。对于这个位置,要么在(A)串删掉,要么在(B)串中删除,要么进行替换,这样就进行了一次操作。因为编辑距离不超过8,所以只要(3^8)枚举前8次操作即可。
效率:(O(n^2 3^8))。
MX Loves Bomb
题目来源
题目大意
给出一棵(n)个点的树,你需要炸毁所有边,每条边只能被炸一次。轰炸有两种类型:
- 选择点对((u,v)),将点(u)到点(v)最短路上的所有边炸毁。
- 选择点(u),将点(u)的所有出边炸毁。
如果需要轰炸的边中有一条之前已经被轰炸,则不能进行该操作。
求最少几次操作可以将所有边都轰炸。
范围:(N le 10^5)。
算法介绍
简化版本1 如果没有第二种操作,且第一种操作可以重复轰炸边,则答案为叶子数除以2向上取整。
简化版本2 如果没有第一种操作,因树是一张二分图,所以答案必然是把二分图左边的点全选或者把右边的全选,取较小者。
简化版本3 如果没有第二种操作,则答案为度数为奇数的点数除以2。
证明 : 若一张连通图能一笔画,则要求度数为奇数的点个数是0个或者2个;因为树是没有环的,所以当所有点度数均为偶数时,轰炸就结束了。每次轰炸可以选择一条一笔画的路径,这就消除了两个奇数点。故答案就是度数为奇数的点数除以2。
原问题 直接树形Dp搞它!
令(F(u,0..1)),表示以点(u)为根的子树,有0或1条边往父亲伸展,将整个子树轰炸的最少步数。随便转移一下即可。
备注
前三个简化版本因为结论简单,可以很轻松地套个限制,出成Dp套Dp。
高等理论计算机科学
题目来源
题目大意
给出一棵(n)个点的树,再给出(m)条树链。求有多少对树链有交。
范围:(n,m le 100000)。
算法介绍
方法一
单纯判断两条树链是否有交,只需要判断第一条链的LCA是否在第二条链上,或者第二条链的LCA是否在第一条链上即可。当两条链的LCA相同时,这两个条件都可以满足;否则两条树链有交只可能满足上述其中一个条件。
对于这(m)条链,在LCA处都加上1;之后对于每条链询问链和即可;可以预处理好每个点到根路径上的和,效率为(O(n))。
方法二
容易发现,两条树链的交仍然是树链;而每条树链中,点数刚好是边数多1。可以由此容斥,将树链的交只贡献1的代价。
具体来说,对于所有点和边,都求出有多少条树链覆盖它;将点的答案减去边的答案即可!效率同样为(O(n))。
交换代数
题目来源
题目大意
给一个长度为(n)的01序列(A),该序列共有(frac{n(n+1)}{2})个区间。进行若干次操作,每次操作会随机选择一个区间,然后将该区间内所有数取反。
求期望多少次操作后,序列(A)变成全0。
范围:(n le 20)。
算法介绍
期望
一般求解期望的题有这样几种思路:
- 期望线性性搞它!
- 高斯消元暴力解!
- 将期望转化为对将所有时刻的概率相加,通常用容斥算概率。
很遗憾,本题似乎不能用以上方法裸算,需要转化模型!
区间异或
对于区间操作,常见的思路就是将序列差分,随之简化为单点操作。
具体来说,我们新建序列(d),令(d_i = A_i igoplus A_{i-1})。这样区间操作就简化为,等概率选择两个不同的(i,j),将(d_i,d_j)取反。
此时惊喜地发现,我们只需要知道全局有几个1就可以轻松转移了!因为最后的目标是将所有数都变成0,而每次操作中,每个位置被选择的概率是一样的。
令(E(i))表示全局有(i)个1时,最终变成全0,期望走多少步。可以很轻松列出方程,直接高斯消元就可以了!
效率:(O(n^3))。
时空阵
题目来源
题目大意
有一张(n)个点的无向图,每条边长度为1。
求有多少图(一共(2^{frac{n(n-1)}{2}})种可能)满足,(1)号点至(n)号点的最短路长度恰好为(m)。
范围:(n,m le 100)。
算法介绍
根据最短路的性质,可以考虑在最短路图上Dp。
具体来说,令(F[i][j][k])表示做到最短图上的第(i)层,图上一共有(j)个点,第(i)层上有(k)个点的方案数。枚举第(i+1)层上有(t)个点,则这(t)个点至少要向第(i)层的点连一条边;同时这(t)个点之间可以互相连边。随便乘个系数就可以转移了。
效率为(O(n^4))。
好排列
题目来源
题目大意
给出长度为(n)的序列(A),求有多少排列(B),使得对于所有(i)都满足:
范围:(n le 10^6)。
算法介绍
这道题的模型相当于给定一张拓扑图,要求合法的标号方案数;据我所知这个问题应该是不能做的。
事实上,根据序列(A),我们可以得到排列(B)的笛卡尔树;换言之,所建立的拓扑图是能够简化成树形态的。
具体建树过程为:
- 建立栈(S),栈中存储的元素,随着下标的增加,权值递减;即有(B_{S_i} > B_{S_{i+1}})。
- 从左到右加入每个元素(A_i),将栈中被(A_i)控制的元素踢出;踢出元素时顺便建立笛卡尔树。
建树之后便是个经典问题,直接DFS遍历树,乘个组合数即可算出答案。复杂度为(O(n))。
字体设计
题目来源
题目大意
给长度为(n)的序列(A),要求选出其中一些位置,使得所有未被选出的位置(x),令它左右两边最近的被选出的位置分别为(l,r)(这两个位置必须存在),满足(A_l < A_x < A_r)或者(A_l > A_x > A_r)。
序列(A)中每个数互不相同。
范围:(n le 10^5)。
算法介绍
算法一
首先(A_1)必须选择,若(A_1 < A_2),则找出最靠左的(x),满足(A_x < A_1),则([2,x-1])均大于(A_1)。显然([2,x-1])中最大数必须选,接着再从这个数开始往右拓展。若(A_1 > A_2),情况类似。
找每个数往右第一个大于(小于)它的数,可以用经典的悬线法(是叫这个名吧)解决,效率(O(n))。
RMQ可以用st表也可以四毛子,效率(O(n log n))或(O(n))。
算法二
考虑递归解决问题,令(Work(l,r))表示区间([l,r])的答案。令(x= min(l,..,r),y= max(l,..,r)),则(x,y)之间的数全部满足要求;只需要继续递归(Work(l,x),Work(y,r))即可(假设(x<y))。
需要支持RMQ操作,效率(O(n log n))或(O(n))。
K个串
题目来源
题目大意
给出长度为(n)的序列(A),对(A)中一个区间([l,r]),它的权值定义为其中不同的数的和,即重复出现的数只算一次权值。
求第(K)大区间的权值。
范围:(n,K le 100000)。
算法介绍
关于第K优解
第(K)优解问题一般有以下三种思路:
- 二分答案 先二分答案,之后验证。若(K)较小,则考虑暴搜出所有解,当解的个数超过(K)时,则停止;若(K)较大,则考虑快速计算出解的个数,一般采用数据结构维护。
- 直接在数据结构上查询 比方说用权值线段树(字母树)在(O(log n))的时间复杂度找出第(K)小(大);用SAM在(O(length))的时间复杂度内找出字典序第(K)小的子串……
- 堆 类似于A*算法,对堆中每个状态都记下当前值和估价值,每次取出当前值加估价值最优的状态进行拓展,一般用数据结构(可持久化堆,主席树等)来维护所有的估价(或者说后继)状态。
简化版本
若序列(A)中所有的元素都不同(即区间([l,r])的权值为(A_l+A_{l+1}+...+A_r)),则这是一个经典问题,可见超级钢琴。
有两种思路:
- 二分答案,枚举左端点,计算有多少右端点合法。这是一个二维数点模型,可以预处理好主席树。复杂度为(O(n log^2 n))。
- 令堆中每一个状态为((l,a,b)),再令(r=RMQ(a,b))。从堆中取出((l,a,b))后,再加入状态((l,a,r-1))和((l,r+1,b))。用st表来询问区间极值(RMQ),复杂度为(O(k log n))。
原问题
本题较超级钢琴多了一个限制,即区间([l,r])的计算方式更为复杂。不能在全局询问(r),而需要对不同的(l)分别对待。
其实这也很好对付,对于每个(l)都建立主席树,主席树上对每个(r)都把区间([l,r])的权值存储下来。令(l)从(n)倒着枚举到(1),先令(l)的主席树从(l+1)处继承。令(next[l])表示(min { x|A_l=A_x }),则(l)的加入,会使得所有(r in (l, next[l]))的区间([l,r]),权值都增加上(A_l)。只需要对主席树进行区间加操作即可。
之后的做法就和简化版本类似了,复杂度为(O((n+k) log n))。