真题总结
写在前面:总结什么?【1】什么思想,什么套路,遇到什么该往哪方面考虑【2】算法:用到了什么算法,超纲或者不超纲,现在可以学【3】技巧:不会怎么打暴力,蒙分
贪心
[N2013积木大赛]:给n个hi高度块,每次可以填平连续块高度为1,求最少的操作次数。
对于序列上的区间问题,可以固定一个端点思考从一边向另一边拓展的贡献
从左向右:
【1】hi+1>hi:cnt+=hi+1
【2】hi+1<=hi,不管
[N2018赛道修建]
最小值的最大先无脑想二分答案,当确定了mid,考虑最优决策使得路径可以出来最大的,依据mid。对于子树rt,儿子最多可以有1条路的贡献(树的性质),传上来最大的,前提是子树内部能自己配对的已经配完了,方法是如果dis>=mid自己就是了,否则选出min,二分出mid-min最小,如果有,ans++,否则min加入上传决策,取max。
一定不会有更优的答案,因为min和min(mid-min)的贡献是1,传上去最多也只有1的贡献,所以不会劣。
动态规划
[N2014花匠]求给定长度n的序列的最长波动子序列(任意数大于或者小于相邻数(左右大小关系相同))
竟然可以转化成递推式子?
\((a[i]>a[i-1])f[i][1]=f[i-1][1]+1 ,else f[i][1]=f[i-1][1]\)
为什么可以只记录比较相邻?贪心,只保留解最宽松的答案
[C2019Emiya今天的饭]
容斥原理,所有合法方案数=所有方案-不合法。
那么不合法可以通过DP简单的维度先算出然后再筛选,还可以在设置维度强制一些东西保证不合法的可选择性,
其实也是化简问题之后,因为同菜的个数<=choose/2,所以不合法的菜最多一格,枚举就行
\(dp[i][j][k]:表示前i天选ci的菜j个,选其他的菜k个方案\),
is_illegal=dp[n][j] [k] (j>k)
压缩维度,本质是相似状态的合并
只关心ci的菜比别的多多少,所以多的一样就是等价的。
\(dp[i][j]:表示选ci的菜,ci比其他多选j种的方案\)
可以省略1维度
[N2015寻找子串]
\(f[i][j][k]:A匹配到i位置,B到j位置,ai=bj,有k个段的方案数\)
\(f[i][j][k]=f[1 to i-len][j-len][k-1],关于len可以倒叙比较(if(same[i-len+1][i]))\)
g前缀和优化转移,观察fijk=fij-1k-1+gj-1k-1,所以直接加不用循环就好了
[C2019划分]
暴力\(dp[i][j]:表示终点是i,上一个段终点是j方案,O(n^3)\)
可以通过打表找决策单调性,发现从dp[rt][1]-->dp[rt][rt-1]的最小值是单调递减的,也就是说对于合法的决策点,一定是尽量选择后面更优,转化成贪心结论,一定是后面划分的段小,(a+b)2>a2+b^2,既保证前面会压缩得更小,后面也小,更优,所以对于每个终点只需要保留一个决策点就是last[i]:最靠近i的合法位置。
考虑合法决策点限制(快速找到合法):
\(s[i]-s[ch]>=s[ch]-s[g[ch]]\),所以维护点\(2*s[x]-s[g[x]]\),单调队列,对于x,x+1满足要求则x不要[这里满足对于位置递增决策点单调不降];插入x,如果前面的key>=x肯定前面不要。
倒序寻找答案避免数组开炸,回溯直接每一段+答案。
模拟
[C2019树上的数]并查集维护有序合并关系和模拟
非常经典的一个问题,就是给你一个序列,给出m个操作,可以把(a,b)交换位置,求一种交换顺序使得最后序列字典序最小。
方法
对于菊花图,发现联通关系构成一个环,如果把sop位置的数字id换到pos位置,那么sop-->pos,发现每个位置指向的位置,就是位置上的数字最后停留的位置,贪心从小数字到大数字枚举位置,并查集防止提前成环和矛盾匹配(不连通ed才能匹配)
对于链图,考虑局部贪心最优解需要对构造有什么限制,是否可以强制限制,检查矛盾进行决策,如果u-->v那么edge(u,x)-edge(x,y)--..edge(yk,v)的优先级是递减的,可以给每个边设置lim[x]:=0表示左edge>右edge,1表示<,-1表示没有,从小到大枚举+检查矛盾。
对于正解,发现如果要求num[u]-->num[v],必须要求u-->nxt的边是u点的边中第一次交换,nxt1-->nxt2的边交换连续,nxt-->v的交换是v边中最后一次,对于强制顺序,并查集在每个点上开一个,边也作为并查集元素,把每次操作分解成out和in的概念表示连续否
【1】u-->nxt:如果跳跃的边in已经把数字换走了就不行;如果环提前自闭也不行
其他类似
注意点的out的意义是第一次换走,in的意义是最后一次换走。
分治
结论
[N2018货币系统]
结论是选出的b集合一定是a集合中的数,所以只需要考虑a中哪些数一定可以被表示出来
[C2019括号树]
求树上合法括号对数,观察模拟(举数)发现一个位置如果是')',那么算上它的贡献就是和他配对的'('的上一个位置的合法方案数+1,(前面合法+它还有它自己新的一个),lst[x]=lst[fa[match]]+1,线性统计贡献。
数据结构维护
[CJX2019散步]
发现问题本质是【1】求每一个时刻所有没有被删除的人距离它的目标最近的是谁【2】把它删除,所在目标--【3】如果目标=0,那么对于后面倒着走的人[i,nxt[i]),目标变成pre[x],距离+dis(pre[x],x);正着走的人,(pre[i],i],目标变成nxt[i],距离+dis(x,nxt[x]).就是线段树区间维护最小值;修改操作只有区间+ 和 覆盖性标记。比较简单的思路是链表+set维护,如果目标是0,先不管,等人拿出来了,如果发现目标已经删除就跳nxt/pre,放进去,再选。费用滞后计算?,反正会大大简化操作
图论
[N2014华容道]经典BFS,有若干格子不能移动,若干格子可以自由移动,一个格子是棋子,要求从当前位置移动到目标位置(必须借助唯一的一个自由空位)。
上来BFS,让空位瞎走,走到棋子,然后棋子也瞎走是暴力做法,但是考虑最优决策的阶段性
【1】先让空位走到棋子旁边【2】然后棋子和空位通过相邻交换的方式走到目标
【1】可以BFSO(n*m)实现,【2】完全可以预处理出图,点\((x,y,k)\)表示棋子在(x,y),空位在k方向的状态,用\(((x-1)*k+y)*4-(4-k)\)标号状态,最后答案跑最短路就可以。
角度转化
[C2019树的重心]
如果直接枚举边计算重心是慢的,发现对于一些边删除是等效的(不让重心改变),枚举每个点作为重心可以被计算多少次贡献,计另一颗子树是S,进一步化简,以重心为根,则对于x点有贡献的边只来自子树外
\((n-S-sx)<=(n-S)/2,mson[x]<=(n-S)/2\)-->\(n-2*s[x]<=S<=n-2*mson[x]\),对于复杂难受的重心判断就变成一个简单限制,数学语言简化问题提炼模型。
树上差分解决路径个数统计:
【1】rt节点路径上的向上子树大小:n-siz[rt]
【2】rt子树内部向下子树大小
【3】所有向下子树大小
2个差分维护,【2】用另一个减去贡献,辅助差分维护,没必要一个上死磕
浙公网安备 33010602011771号