hihocoder题目选讲

图片排版

题目来源

hihocoder 编程练习赛7 A

题目大意

\(n\)张图片,第\(i\)张图片大小为\(A_i \times 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(\text{玄学})\)

子矩阵求和

题目来源

hihocoder 编程练习赛3 D

题目大意

有一个无限大的矩阵\(A\)\(A_{i,j}= \min(i,j)\)
给出\(n,m,P\),找出矩阵\(A\)中一个\(n \times 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 \times 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 \times n\)的01矩阵。进行\(m\)次询问,要求有多少大小为\(a \times 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(\text{玄学})\),因为有效的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 \ne S, T \ne \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 \bigoplus 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)\)

转载于:https://www.cnblogs.com/sunyaofeng/p/6918615.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值