• hihocoder题目选讲


    图片排版

    题目来源

    hihocoder 编程练习赛7 A

    题目大意

    (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))

    震荡数组

    题目来源

    hihocoder 编程练习赛5 C

    题目大意

    对于一个长度为(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{玄学}))

    子矩阵求和

    题目来源

    hihocoder 编程练习赛3 D

    题目大意

    有一个无限大的矩阵(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))

    神奇的子串

    题目来源

    hihoCoder挑战赛27 D

    题目大意

    对于一个长度为(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

    题目来源

    hihoCoder挑战赛25 C

    题目大意

    给一棵(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

    题目来源

    hihoCoder挑战赛24 C
    双倍经验

    题目大意

    给一个长度为(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

    题目来源

    hihoCoder挑战赛24 D

    题目大意

    给出一个(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

    题目来源

    hihoCoder挑战赛23 C

    题目大意

    给一棵(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

    题目来源

    hihoCoder23 挑战赛 D

    题目大意

    对于两棵(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

    题目来源

    hihoCoder挑战赛19 B

    题目大意

    给出(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

    题目来源

    hihoCoder挑战赛19 C

    题目大意

    给出一张(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)中的点,也可能是叶子。即:

    [F(S) = sum_{T subseteq S, T e S, T e emptyset} (-1)^{|T|-1} E(T, S-T) F(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))

    欧拉子图

    题目来源

    hihoCoder挑战赛18 D

    题目大意

    给一张(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

    题目来源

    hihoCoder挑战赛17 C

    题目大意

    给出(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

    题目来源

    hihoCoder挑战赛15 C

    题目大意

    给出一棵(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。

    高等理论计算机科学

    题目来源

    hihoCoder挑战赛11 D

    题目大意

    给出一棵(n)个点的树,再给出(m)条树链。求有多少对树链有交。
    范围:(n,m le 100000)

    算法介绍

    方法一

    单纯判断两条树链是否有交,只需要判断第一条链的LCA是否在第二条链上,或者第二条链的LCA是否在第一条链上即可。当两条链的LCA相同时,这两个条件都可以满足;否则两条树链有交只可能满足上述其中一个条件。
    对于这(m)条链,在LCA处都加上1;之后对于每条链询问链和即可;可以预处理好每个点到根路径上的和,效率为(O(n))

    方法二

    容易发现,两条树链的交仍然是树链;而每条树链中,点数刚好是边数多1。可以由此容斥,将树链的交只贡献1的代价。
    具体来说,对于所有点和边,都求出有多少条树链覆盖它;将点的答案减去边的答案即可!效率同样为(O(n))

    交换代数

    题目来源

    hihoCoder挑战赛11 C

    题目大意

    给一个长度为(n)01序列(A),该序列共有(frac{n(n+1)}{2})个区间。进行若干次操作,每次操作会随机选择一个区间,然后将该区间内所有数取反。
    求期望多少次操作后,序列(A)变成全0。
    范围:(n le 20)

    算法介绍

    期望

    一般求解期望的题有这样几种思路:

    1. 期望线性性搞它!
    2. 高斯消元暴力解!
    3. 将期望转化为对将所有时刻的概率相加,通常用容斥算概率。

    很遗憾,本题似乎不能用以上方法裸算,需要转化模型!

    区间异或

    对于区间操作,常见的思路就是将序列差分,随之简化为单点操作。
    具体来说,我们新建序列(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))

    时空阵

    题目来源

    hihoCoder挑战赛10 D

    题目大意

    有一张(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))

    好排列

    题目来源

    hihoCoder挑战赛9 D

    题目大意

    给出长度为(n)的序列(A),求有多少排列(B),使得对于所有(i)都满足:

    [A_i = max (j mid B_i,B_{i-1},..,B_{i-j+1} le B_i ) ]

    范围:(n le 10^6)

    算法介绍

    这道题的模型相当于给定一张拓扑图,要求合法的标号方案数;据我所知这个问题应该是不能做的。
    事实上,根据序列(A),我们可以得到排列(B)的笛卡尔树;换言之,所建立的拓扑图是能够简化成树形态的。
    具体建树过程为:

    • 建立栈(S),栈中存储的元素,随着下标的增加,权值递减;即有(B_{S_i} > B_{S_{i+1}})
    • 从左到右加入每个元素(A_i),将栈中被(A_i)控制的元素踢出;踢出元素时顺便建立笛卡尔树。

    建树之后便是个经典问题,直接DFS遍历树,乘个组合数即可算出答案。复杂度为(O(n))

    字体设计

    题目来源

    hihoCoder挑战赛5 A

    题目大意

    给长度为(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个串

    题目来源

    hihoCoder挑战赛2 B

    题目大意

    给出长度为(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))

  • 相关阅读:
    使用邮件规则,将收到的邮件进行分类
    QTP连载一:QTP安装及几个重点目录说明
    LR运行QTP脚本
    利用InstallWatch捕获软件操作
    LoadRunner录制时HTML和URL的选择
    QTP连载三:几种参数化的简单比较
    LR学习笔记1(关于数据分析)
    C++生成随机数:正态分布(normal distribution)
    vs编译器生成错误
    wxWidgets 几个实用的系统函数
  • 原文地址:https://www.cnblogs.com/sunyaofeng/p/6918615.html
Copyright © 2020-2023  润新知