中高等DP总结(更新中
1.CF613D Kingdom and its Cities
题意:给定一棵树,每个询问给出一些关键点,要求删掉最少的点使这些点两两不联通,无解输出-1。
思路:先判无解:只要有一个关键点的父亲也是关键点就无解。因为会被删除的点肯定是这些点中一些点的\(lca\),所以考虑建虚树,然后树形DP。具体来讲,设\(g_x\)表示当前有没有关键点与\(x\)直接联通(若\(x\)为关键点则\(g_x=1\)),\(f_x\)表示点\(x\)的答案。转移时先\(f_x=\sum\limits_{y\in son(x)}f_y\),然后根据\(x\)是否是关键点与\(\sum\limits_{y\in son(x)}g_y\)的值(设为\(sum\))分类讨论:
1.\(g_x=0\):
如果\(sum>1\)那就将点\(x\)删去,\(f_x++\);
如果\(sum=1\)那就将\(g_x=1\)
如果\(sum=0\)自然不变
2.\(g_x=1\)那与\(x\)联通的点都得删,\(f_x+=sum\)
最后\(f_1\)就是答案,复杂度\(\sum n\times\log(\sum n)\)。
题意:一次考试共有\(n\)个人参加,可能出现多个人成绩相同的情况。第\(i\)个人说:“有\(a_i\)个人成绩比我高,\(b_i\)个人成绩比我低。”请求出最少有几个人没有说真话。(其实这就是原题面,这么简洁那我不直接复制过来)
思路:(十分人类智慧,不知道是怎么想出来的)首先定义一个人的排名是严格高于他的人数加1,然后就可以把每个限制改成:“我是第\(a_i+1\)名,算上自己共有\(n-a_i-b_i\)个人与我同分”,然后变成区间\([a_i+1,n-b_i]\)表示与第\(i\)个人分数相同的区间,并把这个区间的权值定义为区间长度与所有\(a_j=a_i+1,j=n-b_i\)的\(j\)的个数的较小值。然后我们就把问题转化成了要选出若干不交的区间,最大化权值和。
我们发现,对\(f_i\)做贡献的\(j\)要满足\(r_j<l_i\),因此将所有区间按\(r\)排序后\(j\)就是一个前缀的形式,转移时二分出最大的\(j\),再维护一个前缀最大值即可完成转移。复杂度\(n\log(n)\)。
题意:有若干条形如\((u,v)\)的弦,可以进行若干次操作,每次操作选定\((i,j)\),会切断所有满足\(u>i,j<v\)的弦会被破坏,代价为\(a_i\times b_j\),求破坏所有弦的最小代价。
思路:斜优裸题(只可惜第一步没想出来)先考虑那些弦不会做贡献,很容易发现如果有两个弦\(i,j\)满足\(u_j>u_i,v_j<v_i\)那么如果要删弦\(i\)就一定会同时删弦\(j\),那么弦\(j\)就不做贡献。当我们把所有不做贡献的弦\(j\)删去后,就不会存在一条弦的\(u\)大于另一条弦的\(u\)而\(v\)较小,换而言之就是弦的\(u,v\)都是单增的有了这个性质就很好DP了。
设\(f_i\)为破环前\(i\)个弦的最小代价,转移方程为\(f_i=\min\limits_{j=1}^{i-1}(f_j+\min\limits_{k=1}^{u_j-1}a_k\times\min\limits_{k=v_i+1}^{n}b_k)\)
朴素的转移是\(O(n^2)\)的,我们考虑优化转移。我们发现,这个转移的形式似乎可以斜率优化,把\(f_j\)当成\(y\),把\(\min\limits_{k=1}^{u_j-1}a_k\)当成\(-x\),把\(\min\limits_{k=v_i+1}^{n}b_k\)当成\(k\),就和斜优的形式一模一样了,就可以愉快的套板子了。复杂度\(O(n)\)。
题意:给定序列\(a\),可以任意删除一些数,删除后后面的位置会向前补齐,求最后序列中最多有多少个数满足\(a_i=i\)
思路:我们记\(f_i\)表示钦定最后\(i\)在自己位置上时\(i\)前最多有多少个满足条件的数,然后可得:\(f_i=\max(f_j|j<i,a_j<a_i,a_i-a_j\leqslant i-j)+1\)。朴素转移是\(O(n^2)\)的,我们尝试把合法的\(j\)变成一个前缀的形式。由\(a_i-a_j\leqslant i-j\)可以推出\(j-a_j\leqslant i-a_i\),因此先排除不合法(即\(a_i>i\))的数,再按\(i-a_i\)排序后DP,然后用树状数组维护最大值即可。复杂度\(O(n\log(n))\)
题意:给定一颗树,遍历方向为根到叶子或叶子到根,求遍历方案数。
思路:看到求拓扑序方案数首先想到设\(f[i][j]\)表示\(i\)在\(i\)的子树里拓扑序排名为\(j\)的方案数,然后考虑合并时如何转移。\(f[x][k]=\sum\limits_{i+j=k}f[x][i]\times f[y][j]\times C_{k-1}^{i-1}\times C_{siz[u]+siz[v]-k}^{siz[u]-i}\),这个转移是\(O(n^3)\)的,但是我们发现这个组合数与\(f[y][j]\)无关,也就是说可以用前缀和优化转移,这样复杂度就是\(O(n^2)\)的了。注意转移时根据遍历方向\(j\)的范围会不一样,需简单分个类。
题意:往\(n\times m\)的棋盘上填入\(1\)~\(n\times m\),每个数只能填一次。告诉所有是与其八联通的格子里最小的数的位置,求填数的方案数。
思路:因为所有格子里的数互不相同,所以在\(4\times7\)的棋盘上最多有8个这样的位置(即为局部极小值),这个数据范围提醒我们可以状压。又因为我们要填入数字,我们设DP状态为\(f[i][S]\)表示已经填了\(1\) ~ \(i-1\),当前要填\(i\),填完\(i\)后局部最小值的状态为\(S\),转移的时候我们分两种情况。一种是当前的数填到某给局部最小值的位置,这时直接枚举那个地方还没填数即可。第二种是填到一个不是最小值的位置,因为我们是从小到大填数的,所以局部最小值一定得在周围的数填入之前填入,这样就很方便预处理出每种状态下有多少点可以被填。但我们发现题目中说的是“所有”,这就意味这我们随便填会把一些不是局部最小值的位置变成局部最小值,这是只需大力容斥就行了(反正最多也只能填8个数),用一个不多的方案减去多1个的方案,加上多两个的……就行了。复杂度嘛,反正不高(主要是懒得算)。
题意:给一个序列\(a\),\(a\)中每个数是在\([1,x]\)中的随机数,然后给定\(q\)个询问\(\min\limits_{j=l_i}^{r_i}a_j\),求\(q\)个询问的最大值的期望。
思路:(不愧是黑题)首先,如果一个区间完全包含了另一个区间,那它的最小值一定不大于它包含的区间的最小值,那么它就不做贡献。如果直接求期望没什么好方法,我们考虑把它转化为一个计数问题,最后除以\(x^n\)就是答案。设\(h_i\)为最终答案\(\leqslant i\)的方案数,然后最终答案为\(i\)的方案数就是\(h_i-h_{i-1}\),这等价与每个区间的最小值都\(\leqslant i\),即每个区间都存在\(\leqslant i\)的数,我们设\(g_j\)表示在\([1,n]\)内选出\(j\)个点,是的每个区间都被覆盖的方案数,然后\(h_i\)就等于\(g_j\times i^j\times (x-i)^{n-j}\)。然后考虑用DP算出\(g_j\)。我们设\(f[i][j]\)表示前\(i\)给位置放了\(j\)个点,且第\(i\)个位置必须放点,覆盖了所有左端点\(\leqslant i\)的区间的方案数。先预处理出\(fl[i]\)和\(fr[i]\)分别表示覆盖了\(i\)的最左/右的区间,如果没有就是左侧最接近它的区间。因此\(f[i][j]=\sum\limits_{fr[k]+1>=fl[i]}f[k][j-1]\),容易发现\(k\)处在一个区间内,因此可以前缀和优化。然后就得到\(g_j=\sum\limits_{fr[i]=q}f[i][j]\),然后也可以算出\(h_i\)。复杂度\(O(n^2)\)。
题意:有长度为\(n\)的01串和\(l\)个长度,每次可以将某个长度的子区间的取反,初始全是\(0\),求把给定的\(k\)给位置变成\(1\)的最小步数。
思路:(把状压和最短路结合起来的题目好像很罕见)看到\(k\leqslant 10\)就想到是状压,但是区间取反不好维护,我们考虑差分,即每个数异或上前一个数,那区间取反可以变成两个单点取反。因为只有\(k\)个点最后为1,所以也最多只会有\(2k\)个数最后差分数组为\(1\)。设\(f[S]\)为把01串变成状态\(S\)的最小步数,因此\(f[S]+cost(i,j)\rightarrow f[S|(1<<i)|(1<<j)]\)其中\(cost(i,j)\)表示只把\(i,j\)取反要花费的最小代价。我们考虑每进行一次操作是把相隔为\(a_i\)的两个位置取反,而我们取反\(l,r\)再取反\(r,l'\),就相当与对\(l,l'\)进行了取反,然后我们发现这很像一个最短路问题,每个点向\(x+a_i\)与\(x-a_i\)连距离为1的边,再用\(bfs\)求一遍最短路即可求出\(cost(i,j)\)。总复杂度\(O(nm+2^{2k})\)。
题意:有一个\(n\times m\)的0/1表格,可以任意将一行或一列的0/1翻转,求最终最少有多少个1。
思路:我们发现\(n\)只有20,就想到了状压DP。对于行的不同翻转状态只有\(2^n\)种,我们自然想到了设\(dp[S]\)表示行的翻转状态为\(S\)时的答案。设每一列的状态为\(a_i\),再设辅助数组\(f[i]\)表示列状态为\(i\)时翻转后最少有多少个1,即\(\min(ppc[i],n-ppc[i])\),那么\(dp[S]=\sum\limits_{i=1}^m f[S\oplus a_i]\)。但这样的复杂度是\(O(m2^n)\)的,明显过不了,我们考虑如何优化。我们考虑枚举答案\(y=S\oplus a_i\),那么 \(dp[S]=\sum\limits_{i=1}^m \sum\limits_{y=0}^{2^n-1}[y==a_i\oplus S]f[y]\),但前面的\(\sum\limits_{i=1}^m\)还没丢掉。我们设\(Q[i]\)表示有多少列的状态为\(i\),那么\(dp[S]=\sum\limits_{i=0}^{2^n-1}\sum\limits_{y=0}^{2^n-1}[i\oplus y==S]Q[i]\times f[y]\),换个写法就是\(dp[S]=\sum\limits_{i\oplus y=S}Q[i]\times f[y]\),这个形式是不是跟\(FWT\)长得一模一样?然后就可以愉快的套板子了。复杂度\(O(n\log(n))\)。
10.CF1428G2 Lucky Numbers (Hard Version)
题意:定义一个数的权值为:若第\(i\)位(个位算第0位)为3的倍数,则产生\(\frac{x}{3}\times a_i\)的贡献,多次询问一个数被拆成\(k\)个数产生的总贡献最大值。
思路:(什么神必多重背包+分组背包) 首先发现一个数位上是3还是6还是9是没有影响的,所以可以把6和9当成3来处理(减小思维难度及码量)。接着我们发现可以把这\(k\)个数中的\(k-1\)个每一位都是\(3\)的倍数,然后考虑剩下的一个有数位不做贡献的数。先考虑这\(k-1\)个数,我们把分成\(k-1\)个数之和反过来看,即由这\(k-1\)个数组成当前数,再把每一个数位拆开,发现这可以当作一个多重背包,第\(i\)个物品的价值为\(a_i\),体积为\(10^i\),有\(3\times(k-1)\)个,但因为\(k\)很大,所以采用二进制分组优化。再考虑剩下的一个数,就简单的当作一个分组背包来做,不过要注意这时也有可能还有\(3\)的倍数的数位。复杂度\(O(n\log(k)+(60?)n)\)。(还可以顺带把Easy Version写了,双倍经验)
11.CF1393E2 Twilight and Ancient Scroll (harder version)
题意:\(n\)个字符串,每个字符串可以删掉一个或者不删,问有多少种方案是得字典序不降。
思路:(这题明明就是考串串科技)首先可以想到一个朴素的\(\sum\left|S\right|^3\)的DP,设\(f[i][j]\)为第\(i\)个串删第\(j\)个字符使得这东西字典序不降的方案数,若不删则\(j=len+1\)。然后考虑优化转移。首先想到,可以把字符串想作一个数,比它小的数是一个前缀的形式,那字符串也是一样,因此把字符串删掉一个之后的\(len\)个字符串排序后转移就可以用前缀和优化了。但是这样空间显然开不下,时间上也过不去。我们采用贪心的思想,考虑相邻的两位,他们的前缀是相等的,如果这两位相等,那删掉后的字符串也相等,若不相等,就可以比出大小,最后稍稍处理一下删第\(n+1\)个字符的情况就可以\(O(n)\)了。然后我们考虑不同字符串之间怎么高效比大小,这时就可以用哈希+二分,找到两个串第一个不相等的位置,就可以比大小了,单次\(O(\log\sum\left|S\right|)\),共\(O(\sum\left|S\right|)\)次,因此总复杂度\(O(\sum\left|S\right|\log\sum\left|S\right|+\sum\left|S\right|)\)。(又是一道双倍经验的题,好耶)
题目:给定数字串\(s,x,y(len(x)=len(y),x\leqslant y)\),求存在长度不小于\(\left\lfloor\dfrac{len(x)}{2}\right\rfloor\)的子串是\(s\)的子串的数字串\(t\in[x,y]\)的数量。
思路:(为了这道题我甚至专门学了半天的SAM)。首先看到\([x,y]\)第一反应肯定是数位DP,但又因为与字符串匹配有关,自然就考虑上AC自动机或SAM,而我写的是SAM的做法。设\(f[cur][pos][len][lim][flag]\)表示当前在第\(cur\)位,在SAM上为第\(pos\)个节点,当前已匹配的字符串长度为\(len\),\(lim=0/1\)表示有没有顶到上界,\(flag=0/1\)表示目前长度合不合法。转移时枚举下一个选的哪一个数字,若长度已合法或SAM上当前节点有这个儿子则可直接转移,否则不断跳\(link\)直到有这个儿子或跳到了1号节点为止,然后再转移。复杂度\(O(10nd^2)\)。(又一次SAM忘开两倍空间了,警钟敲烂)
题意:定义一个序列的权值为两个数之差的绝对值的最小值,求长为\(n\)的序列\(a\)的所有长为\(k\)的子序列的权值之和
思路:首先肯定是对\(a\)数组排序。我们发现,这一题的值域很小,那做法可能和值域有关。答案的最大值肯定不会超过\(\dfrac{a_n}{k-1}\),因此我们考虑枚举答案\(v\),然后DP计算权值\(\geqslant v\)的方案数,然后直接将方案数相加即可。然后设\(f[i][j]\)表示当前在第\(i\)个数且选第\(i\)个数,已经选了\(j\)个且满足条件的方案数,所以\(f[i][j]=\sum\limits_{a_i-a_k\geqslant v}f[k][j-1]\),然后用前缀和优化就是\(O(nk)\)的了。总复杂度\(O(\dfrac{V}{k}nk)=O(nV)\)。
14.CF1179D Fedor Runs for President
题意:在树上删除一条边后,求最多能形成多少条简单路径。
思路:(感觉这题的难度比前几题上了一个台阶) 大体做法:树上斜率优化。(实在难以理解) 首先只考虑加一条边后会多出来多少条路径。假设加入的边为\((x,y)\),设\(s_k\)为\(path(x,y)\)上一点\(k\)不向路径延伸的子树大小,那
\]
\]
\]
要让答案最大,我们就要选一条路径使\(\sum\limits_{k\in path(x,y)}s_k^2\)尽量小。
我们设\(x\)为当前点,然后计算每一条经过\(x\)的路径的答案。设\(f_x\)为\(x\)的子树中让上式最小的路径,则\(f_x=\min\limits_{y\in son(x)}f_y+(size_x-size_y)^2\)其中\(size_x-size_y\)即为\(s_x\)。
求出这个后,考虑如何将两条路径合并。先朴素直接枚举两个儿子来转移,即
\]
这样一次转移是\(O(son_x^2)\)的,但看到这个二次式我们尝试用斜率优化。我们枚举一个\(u\),然后求出最优决策点\(v\)。枚举两个点\(i,j\)假设\(i\)比\(j\)更优,那么有
\]
化简后可得
\]
再变形可得
\]
因此当\(size_i>size_j\)时,
\]
然后再注意\(size_i<size_j\)时不等号变号,以及特判一下等于即可。还有就是我们按\(size\)大小先给所有儿子排序一遍后转移就很方便了。
总复杂度\(O(n\log(n))\)(感兴趣的话可以自己证一下复杂度,我太懒了这里就不证啦)
upd:其实很好证。
15.CF115D Unambiguous Arithmetic Expression
题意:给一个不含括号的表达式,求有多少种方法给其加上括号使得其仍合法。
思路:(一眼区间DP,只是\(n\leqslant 2000\)) 不过好像区间DP减减枝可以过,但我没试。设\(dp[i][j][0/1]\)表示考虑到第\(i\)个位置,前面已经有\(j\)个左括号为匹配,当前填的是"("还是")",然后考虑转移,方向是\(dp[i][j][0/1]\rightarrow dp\cdots\)。先考虑\(dp[i][j][1]\),那么有\(dp[i][j][1]\rightarrow dp[i+1][j+1][0]\)(在后面加一个左括号就是\(j+1\)个左括号未匹配),\(dp[i][j][1]\rightarrow dp[i][j-1][1]\)(在自己这里再添一个右括号)。然后是\(dp[i][j][0]\),那么有
\]
然后这道题就做完了。复杂度\(O(n^2)。\)
题意:给定有\(n\)个点的数和无向图,求有多少种标号方案使得树上每一条边都在无向图中存在。
思路:考虑到求编号,我们设\(f[i][j][S]\)表示\(i\)子树内,\(i\)标号为\(j\),且使用了\(S\)中的标号的方案数,但转移时要枚举子集,复杂度是\(O(n^3\times3^n)\)的,过不去,我们考虑优化。首先很容易想到把标号集\(S\)的限制去掉,但这样会出现重复编号,那容斥不就解决了吗?具体来说,对于每个\(S\in(0,2^n-1)\)求出每个点的编号都在\(S\)内的方案数,然后用\(\left|S\right|=n\)的方案数减掉\(\left|S\right|=n-1\)的,再加上\(\left|S\right|=n-2\)的\(\cdots\)然后这题就做完了。复杂度\(O(n^3\times2^n)\)
题意:定义一个排列的波动值为相邻两数之差的绝对值之和,求所有长为\(n\)的排列波浪值不小于\(m\)的概率,保留小数点后\(k\)位。
思路:(上古时期模拟赛考过简单版,不出意外获得了0分的好成绩) 看到绝对值不好处理,一个常见的套路是考虑从小到大插入每个数依次计算贡献。我们发现一个数\(i\)作的贡献只和前\(i-1\)个数组成的连续段情况以及和边界的关系有关。我们设\(dp[i][j][k][l]\)表示考虑到第\(i\)个数,一共组成了\(j\)个连续段,总贡献为\(k\),有\(l\)个连续段与边界相连。然后考虑转移。
1.\(i\)不与任何连续段相连,那么连续段数+1,贡献减少\(2i\),边界情况不变,有\(j+1-l\)种方案。
2.\(i\)一边与边界相连,另一边不与连续段相连,那么连续段数+1,贡献减少\(i\),边界上的\(l+1\),方案数有\(2-l\)。
3.\(i\)一边与连续段相连,另一边不与连续段相连,那么连续段数不变,贡献不变,\(l\)也不变,有\(2j-l\)种方案。
4.\(i\)一边与边界相连,另一边与连续段相连,那么连续段数不变,贡献\(+i\),边界的\(l+1\),有\(2-l\)种方案。
5.\(i\)两边都与连续段相连,显然连续段数减少1,产生\(2i\)的贡献,边界情况不变,有\(j-1\)种方案。
总复杂度\(O(n^4)\)。
坑点:小数点后30位要用__float128,边转移边除以\(i\),以免除以\(n!\)时爆精度,还有要用滚动数组。
题意:在树上选出\(k+1\)条不相交的链使得链的边权和最大。
思路:WQS二分+树形DP。看到恰好多少条就想到了WQS二分,二分选出一条链的代价,然后用树形DP算出选出多少条链最优。具体的,设\(dp[i][0/1/2][0/1]\)表示在\(i\)的子树中,点\(i\)的度数为0/1/2,最后的0/1表示是权值还是选了几条链。转移时:
\]
\]
\]
复杂度:\(O(n\log(n))\)。
题意:给定一个序列\(h\)表示每个城市的水量,保证\(h_i\)互不相等,可以进行\(k\)次操作,每次可以任意指定一些位置并把每个位置变成它们的平均数,求1号城市的最大值(保留\(p\)位整数)。
思路:(看完这道题38页的PPT,我整个人都被震撼到了) 首先可以找一大堆性质。比如,比1号城市小的水量肯定没用;合并时一个一个合并比一起合并更优;一个城市最多和1号城市联通一次;每次合并一定包含1号城市;当次数不足以每次都一个一个合并时,一定是将\(h\)排序后的连续一段。然后就可以DP了。设\(f[i][j]\)表示前\(i\)个位置合并了\(j\)次的最优值,那么\(f[i][j]=\max\limits_{k<i}\dfrac{f[k][j-1]+s_i-s_k}{i-k+1}\)。这样的复杂度是\(O(n^2kp)\)的,然后发现决策点是单调的,这样就可以斜率优化,复杂度\(O(nkp)\),但我们发现这还过不去。还有性质是选择的区间大小不升,并且由每个\(h_i\)互不相同可以推出长度不为1的区间不会超过\(O(\log\frac{nh}{\triangle})\),其中\(\triangle=\min\limits_ih_i-h_{i-1}\) (反正我不会证),然后复杂度就是\(O(np\log(nh))\)。如果在前面DP时不直接使用高精度运算就可以把复杂度降为\(O(n(\log^3nh+p))\)。点评:毒瘤题
题意:一个人从\((0,0,0)\)走到\((n,m,r)\),每一步可以\((x,y,z)\rightarrow(x',y,z)(x\&x'=x)\)或\((x,y,z)\rightarrow(x,y',z)(y\&y'=y)\)或\((x,y,z)\rightarrow(x,y,z')(x\&z'=z)\),其中有\(m\)个障碍点,求方案数。
思路:考虑容斥,用全部的走法减去不合法的走法。先考虑怎么求全部走法。设\(dp[i][j][k]\)表示走到点\((x,y,z)\)满足\(popcount(x)=i,popcount(y)=j,popcount(z)=k\)的方案数,转移时
\]
先把所有关键点按字典序排序,然后依次枚举每个障碍点\(i\),设\(f[i]\)表示不经过任何障碍点到达\(i\)的方案数,因此:
\]
其中\(to(j,i)\)表示从障碍点\(j\)到障碍点\(i\)的方案数,则
\]
复杂度\(O(\log^4n+m^2)\)。
21.CF1119F Niyaz and Small Degrees
题意:一棵大小为\(n\)的树,有边权,设\(f(x)\)表示要满足所有点的\(deg\leqslant x\)所要删掉的边的边权和的最小值,求出\(f(0)\)到\(f(n)\)
思路:先考虑对于每个\(x\)计算答案。设\(dp[i][0/1]\)表示\(i\)向上连的边删或不删时的最小代价。转移时,对于\(i\)的每个儿子\(j\),有两种贡献,\(a_1=dp[j][0]+w\)表示删掉\((i,j)\)的贡献,\(a_2=dp[j][1]\)表示不删\((i,j)\)的贡献。我们考虑先取所有的\(a_2\),用堆来维护所有的\(a_1-a_2\),然后选最小的一些\(a_1-a_2\)把\(a_2\)替换掉,这一步可以用堆维护。单次复杂度\(O(n\log n)\)。
然后考虑正解。如果从小到大考虑每个\(x\),那么一个\(deg=x\)的点对\([x,n-1]\)是没有贡献的,对于每个点只需考虑\(x<deg\)的情况,这样的量级是\(\sum deg=n\)的。我们将\(deg\leqslant x\)的点视为无用点,其他为有用点,然后从每个有用点开始dfs,把无用点视为叶子,这时每个无用点的\(a_1=w,a_2=0\),它对它相邻的有用点的贡献即为\(a_1-a_2=w\),然后像暴力做法一样加入有用点的贡献,最后再加上一直弹堆顶到堆中元素为须删掉的边数时,堆中所有\(a_1-a_2\)的和就是答案。注意更新答案后要撤销加入的有用点的贡献,并撤销不断弹出堆顶直到堆中元素符合要求时删除的无用点的贡献,需要用可删除堆维护。总(均摊)复杂度\(O(n\log n)\)。(有个智障调了一上午结果发现可删除堆写错了,警钟敲烂)
题意:有\(n\)个位置,每个位置上可以选一个在\([l_i,r_i]\)中间的数,并且要大于它前面任意一个选了的数,也可以不选,求方案数。
思路:先考虑朴素DP。设\(f[i][j]\)表示第\(i\)个位置选了\(j\)的方案数,那么
\]
我们发现如果把第二维\(j\)看作自变量的话,那么可以发现这是一个多项式,而每求一次前缀和次数+1,所以最高是\(n\)次多项式,那么我们带入\(n+1\)个点值进去就可以得到答案。
复杂度\(O(n^3)\)。
题意:给定一个无向仙人掌,边权互不相同,定义\((u,v)\)是好的当且仅当存在从\(u\)到\(v\)的路径满足边权单调递增,对于所有\(u\)求出有多少\((u,v)\)是好的。
思路:首先考虑如果是一棵树怎么做。我们从大到小加边,设\(f_i\)为答案,每次加的边\((x,y)\)的两端在之前必然无法到达,所以\(u\)能到的点多了\(v\)和\(v\)能到的点,即\(f_x=f_y=f_x+f_y+1\)。
然后考虑一般的情况。我们发现,有可能加边的两端已经互相可达,这时会出现重复,而这种情况只可能出现在最小边的两端可以合法到达最大边的环中,设最大边\((u,v)\)的一段为\(u\),那贡献要减去加入这条边时的贡献,即\(f_x=f_y=f_x+f_y+1-g[u]\),其中\(g_u\)为加入这条边时的贡献,即\(g_u=f_u+1\)。先求出点双然后判断每个点双是否合法即可。
复杂度\(O(n)\)。
题意:给一个\(2\times n\)的棋盘和\(2\times n\)个数,给出把这\(2\times n\)个数填入棋盘的方案,使得从左上角只向下或右走走到右下角遇到的数的和的最大值最小。
思路:(思维量巨大题) 先找一些性质。首先,假设选好了一行的数,那么肯定是第一行从小到大,第二行从大到小。假设在第\(k\)列转向的答案为\(ans_k\),那么\(ans_k=ans_1+\sum\limits_{i=1}^{k-1}w_i\),其中\(w_i=a_{i+1}-b_i\),即\(k\)向右移一位的改变量。容易知道\(w\)是递增的,而一开始的\(w_1\)有可能小于0,那么只有可能在\(k=1\)或\(k=n\)是取到最大值。接着,假设我们选定了\(a_1,b_n\),那么我们就要选出\(n-1\)个数使得\(\max(\sum a_i+a_1,\sum b_i+b_n)\)最小,而\(\sum a_i+\sum b_i\)的值是确定的(记为\(S\)),假设一个方案\(\sum a_i=x\),那\(\sum b_i=S-x\),所以最接近\(\left\lfloor\dfrac{S}{2}\right\rfloor\)的一组方案是最优的。这样,我们枚举\(a_1,b_n\),就得到了\(O(n^4\sum a_i)\)的做法,但明显过不了。我们要让最大值最小,那么一定要让做贡献的数尽量小,所以一个很直观的想法是把最小值和次小值放在\(a_1\)和\(b_n\),这样就可以降低复杂度了。
复杂度\(O(n^2\sum a_i)\)。
题意:给一个序列\(h\),要分成若干段使得每一段长度不大于\(m\),最小化所有段的最大值之和。(其实这题本来不是这个题单里的)
思路:(\(O(n)\)的做法太神仙了) 首先可以很简单的想到一个\(O(n^2)\)的DP:设\(f_i\)表示\(i\)为当前段末尾的最小和,则\(f_i=\min\limits_{s_i-s_j<=m}(f_j+\max\limits_{k=j+1}^ih_k)\)。因为有最大值,我们考虑找找单调性。首先,如果\(h_j\leqslant h_{j+1}\),那么\(\max\limits_{k=j}^i h_k=\max\limits_{k=j+1}^i h_k\),而\(f\)显然是单调递增,那这个时候从\(j\)转移一定比从\(j+1\)转移更优,所以我们很容易用一个单调队列(记为\(q\))来记录可能对答案做贡献的位置。然后我们考虑如何计算贡献。因为队列里的值是递减的,即\(h_{q_l}>\cdots h_{q_r}\),那么每个的贡献就是\(f_{q_l}+a_{q_{l+1}}\),这时我们还需维护\(f\)。为了保证复杂度,我们可以用两个单调下降的栈来维护\(f\),每当左或右端点到了中点就重构,这样复杂度就是\(O(n)\)的了。
26.P8352 [SDOI/SXOI2022] 小 N 的独立集(其实这题本来也不是这个题单里的)
题意:给定大小为\(n\)的树,每个点点权可以为\([1,k]\),求最大权独立集大小为\(i\in[1,nk]\)的方案数。
思路:一开始以为是普通的树形dp,后来才发现是dp套dp。首先考虑如果是直接求最大权独立集大小,那么就是设\(f[x][0/1]\)表示\(x\)点选/不选的最大值,然后\(f[x][0]=\sum\limits_{y\in son(x)}\max(f[y][0],f[y][1])\),\(f[x][1]=val[x]+\sum\limits_{y\in son(x)}f[y][0]\),而在计数时,我们考虑将\(f[x][0]\)和\(f[y][0]\)记入状态,设\(g[x][v0][v1]\)表示\(x\)子树中\(f[x][0],f[y][0]\)分别为\(v0,v1\)的方案数,这就是一个树上背包的问题,只不过复杂度较高。我们发现这个状态本身就是\(O(n^3k^2)\)的,需要优化,于是我们改变定义,设\(f[x][1/0]\)表示是否强制不选点\(x\)的答案,我们发现\(f[x]\)有一些性质,比如\(0\leqslant f[x][0]-f[x][1]\leqslant val[x]\leqslant k\),那么我们就可以简化状态,记\(g[x][v][d]\)表示在\(x\)的子树中,\(f[x][0]\)为\(v+d\),\(f[x][1]\)为\(v\)的方案数,每次转移时有\(g'[x][i+p+q][\max(i+j+p,i+p+q)-(i+p+q)]+g[x][i][j]\times g[y][p][q]\rightarrow g'[x][i+p+q][\max(i+j+p,i+p+q)-(i+p+q)]\),这样一来复杂度就是\(O(n^2k^4)\)的了。
27.P8329 [ZJOI2022] 树(同上……)
题意:定义两颗树是好的当且仅当第1棵树每个点父亲编号小于它,第2棵树每个点父亲编号大于它,每个点恰好在一棵树上是叶子,求对1~\(n\)求方案数。
思路:(不会容斥,好似) 首先考虑设\(f(S)\)表示第1棵树非叶集合为\(S\)的方案数,第二棵树为\(g(S)\),那么很容易推出
\]
但是\(f(S)\)和\(g(S)\)并不方便直接求,我们考虑容斥。设\(f'(S)\)表示第一棵树非叶集合\(\subseteq S\)的方案数,\(g'(S)\)同理,那么可知
\]
这样似乎比之前的恰好更好处理一些。于是我们设\(dp[i][j][k]\)表示\(|\{1,2,\cdots i\}\cap S'|=j,|\{i+1,i+2,\cdots n\}\cap T'|=k\)的方案数。转移时根据\(i\)是属于\(S'\)还是\(T'\)还是都不属于进行分类讨论即可。
复杂度\(O(n^3)\)。
题意:给出\(a\)数组以及\(b\)数组的总和,求有多少种标号方案,使得按\(b_i\)从小到大依次加入\(b_i\)后新加入的\(a_i+b_i\)是当前最大的。
思路:(学到了一个很新的科技) 我们考虑DP的状态应该与那些有关,首先是已经加入了的位置有哪些,这个是\(O(2^n)\)的,当前加入的数的\(b_i\)以及总和\(sum\),这样的DP固然带\(2^n\times m^2\),显然不行,我们考虑怎么去掉一维。我们可以发现,一旦确定了顺序,我们就可以贪心地使每次的\(b\)尽量小,这也启示我们可以试图去掉当前枚举的\(b_i\)这一维,只需保证\(b_i\)不降,即\(b_i\)的差分数组非负。但是有一个很严重的问题是\(b\)是有后效性的,于是我们可以用“费用提前”的\(trick\),把当前选的数对后面的数的影响算出来并减掉,这样就可以避免有后效性了。时间复杂度\(O(2^nn^2m)\)。
题意:有\(n\)堆石子,每一堆石子数量在\([l_i,r_i]\)之间,每次操作可以拿走一堆里\(\geqslant2\)个石子或在长度\(\geqslant3\)的区间里每一堆石子拿走一个,求添加恰好\(k\)个石子后能把所有石子取空的方案数。
思路:(不愧是NOID1T2,听同学讲后又看了Wei老师的博客才搞懂)
首先考虑如何检验一个局面\((k=0)\)是否合法。首先考虑简化操作。第二个操作显然可以把长度\(>6\)的区间拆成由长度为\(3,4,5\)的区间拼起来,那么我们就只用考虑这4种操作。其次,我们可以使每一种长度的操作的次数\(\leqslant1\),因为做相同的操作可以直接用操作1代替。于是就可以考虑DP,设\(f[i][j][k][l]\)表示\(i\)之前的操作二,有\(j\)个可以延伸到\(i\),有\(k\)个必须枚举到\(i\),有\(l\)个必须枚举到\(i+1\),是否可行。转移时枚举从\(i\)开始的操作数量\(p\),再枚举\(j\)个中实际延伸到\(i\)的数量\(q\),判断\(a_i-k-l-p-q\)是否\(=0\)或\(>1\)就是合法的。
这时我们发现,可以将\(j,k\)这两维状态合并。我们直接钦定有多少个操作延伸到了\(i\)即可。于是,我们改成设\(f[i][j][k]\)表示\(i\)之前的操作二,有\(j\)个钦定延伸到\(i\),有\(k\)个钦定延伸到\(i+1\),是否可行。转移时同样枚举从\(i\)开始有多少个操作二即可。
这个时候\(j,k\)的范围是\(j\leqslant6,k\leqslant3\)的,我们考虑缩小范围。我们发现,如果同时进行了一次\(4,5\)的操作,那这等价于一次操作1和下一个位置的\(3,4\)的操作,于是就降到了\(j\leqslant4,k\leqslant2\)。接着我们枚举从\(i-1\)延伸到\(i\)且在\(i\)处长度\(\geqslant3\)的情况可以发现这些可以用不超过2次操作二代替,于是范围就是\(j\leqslant2,k\leqslant2\)的了。
接着考虑\(k\neq0\)的情况。我们发现恰好不好处理,于是改为至多,再容斥一下下,即添加小于\(k\)个石子有解,添加\(k\)个后无解的方案数。首先\(k=0\)无影响,\(k=1\)只有\(n=3\&\&a_1=a_2=a_3=1\)或\(a_i=0\),而当\(k\geqslant2\)时可以归纳证明若添加小于\(k\)个石子有解,那么添加\(k\)个石子也一定有解(k=1除外)。于是我们设\(g[i][j][k]\)表示使\(f[i][j][k]=1\)最少需添加的石子数,转移时枚举\(l\),然后从\(g[i][j][k]+\)至少要添加的石子数转移到\(g[i+1][p][l]\)即可。
接下来就是对\(k=0\)计数。因为\(f_i\)只有9种状态,于是我们考虑DP套DP。设\(dp[i][S]\)表示这9种状态取值为\(S\)的方案数。为了方便转移,我们设\(get(S,j)\)表示从状态为\(S\)的\(f_i\)开始,令\(a_i=j\),转移到\(f_{i+1}\)的状态。由于\(g[i][j][k]\)中\(j,k,l\leqslant2\),因此\(a_i\leqslant8\),这样子贡献就比较好统计了。然后(不知道怎么样子就) 发现\(\geqslant6\)的操作是等价的,于是就可以\(O(nS)\)计算了。
最后考虑\(k>0\),因为\(g[i][j][k]\)有\(0\)~\(101\)共102种取值,但实际只有\(S=8765\)种状态,可以通过。
复杂度:\(O(nS)\)。(写得好累……有1302个字,比作文还长)
中高等DP总结(更新中的更多相关文章
- 用Javascript方式实现LeetCode中的算法(更新中)
前一段时间抽空去参加面试,面试官一开始让我做一道题,他看完之后,让我回答一下这个题的时间复杂度并优化一下,当时的我虽然明白什么是时间复杂度,但不知道是怎么计算的,一开局出师不利,然后没然后了,有一次我 ...
- 31 ArcGIS中后缀一览表(持续更新中……)
- Visual Studio扩展与更新中插件被禁用,安装后无法使用
在Visual Studio中的扩展与更新中安装插件后,显示[禁用],重新安装后仍然不能使用,但是VS默认安装的扩展却可以正常使用. 这里需要注意下方显示“当前不允许加载每用户扩展”,点击“启用每用户 ...
- 在UPDATE中更新TOP条数据以及UPDATE更新中使用ORDER BY
正常查询语句中TOP的运用: SELECT TOP 1000 * FROM MP_MemberGrade 随意更新一张表中满足条件的前N条数据: UPDATE TOP (1) MP_Member ...
- git常用命令(持续更新中)
git常用命令(持续更新中) 本地仓库操作git int 初始化本地仓库git add . ...
- Atom使用记录(持续更新中)
部分内容取自:http://www.jianshu.com/p/dd97cbb3c22d,我自己也在使用,持续更新中 Atom安装插件在窗口中File---Setting---install 在里面进 ...
- [Linux] Linux指令汇总(持续更新中...)
写在前面: 以前真心没有玩过Linux系统,总感觉整天摆弄Linux的同学都是大牛.如今,在公司里实习需要远程登录Linux服务器,所有的代码都要在开发板上完成,所以被逼无奈也不得不定下心来好好学学L ...
- Pig基础学习【持续更新中】
*本文参考了Pig官方文档以及已有的一些博客,并加上了自己的一些知识性的理解.目前正在持续更新中.* Pig作为一种处理大规模数据的高级查询语言,底层是转换成MapReduce实现的,可以作为MapR ...
- Pig语言基础-【持续更新中】
***本文参考了Pig官方文档以及已有的一些博客,并加上了自己的一些知识性的理解.目前正在持续更新中.*** Pig作为一种处理大规模数据的高级查询语言,底层是转换成MapReduce实现的, ...
- java视频教程 Java自学视频整理(持续更新中...)
视频教程,马士兵java视频教程,java视频 1.Java基础视频 <张孝祥JAVA视频教程>完整版[RMVB](东西网) 历经5年锤炼(史上最适合初学者入门的Java基础视频)(传智播 ...
随机推荐
- 使用Wireshark完成实验2-TCP
1.打开Google Chorme,进入https://gaia.cs.umass.edu/wireshark-labs/alice.txt 2.将文本保存,进入https://gaia.cs.uma ...
- 廖雪峰JS知识点整理——快速入门
基本语法 1.每个语句以:结尾. 2.单行注释://... 3.多行注释:/*... ...*/ 数据类型和变量 运算 1.==自动转换数据类型在比较,不推荐使用 2.===不会转换数据类型,推荐使用 ...
- c++学习8 动态空间申请
一 动态分配内存的概述 在数组一幕中,介绍过数组的长度是事先预定好的,在整个程序中固定不变.但是在实际的编程过程中,往往会发生这种情况:我们并不清楚到底需要多少数目的空间,而且无法事先预定,所以对了应 ...
- MySQL数据库架构&SQL注入漏洞
1.查找zblog数据库中有哪些表
- Vue.js + TypeScript 项目构建
一:全局安装vue/cli npm install -g @vue/cli安装完成后检查版本vue --version 二:构建项目创建文件 vue create projectName 有的刚开始 ...
- resttemplate 由于框架原因自带了转xml方式,不改变框架底层情况下,修复为返回json格式
RestTemplate httpClientTemplate = new RestTemplate(); List<HttpMessageConverter<?>> conv ...
- Leetcode61
!!Given the head of a linked list, rotate the list to the right by k places.!! # Definition for si ...
- 2月23日javaweb之Maven
Maven常用命令 compile:编译 clean:清理 test:测试 package:打包 install:安装 Maven生命周期 Maven对项目构建的生命周期描述是一次构建过程经历了多少个 ...
- Python自学day03
1.数据类型 int 1,2,3用于计算 bool True,False用于判断 str 存储少量数据,用于操作 list 存储大量数据 [1,2,3,'a ...
- Matlab字体设置中找不到字体的解决方法(转载)
Matlab字体设置中找不到字体 Matlab默认的字体实在不好看,一般都需要重新设置字体. 在其字体设置中有些字体不能同时支持中文和英文,我在之前的博客中说过,如何为Matlab设置一款好看的同时兼 ...