Link
Solution
贪心+模拟,每次取剩下的个数最大的,在最大的里面如果有,就先取。
被priority_queueFunctor的奇葩规则坑了一会儿。
Code
1 |
|
贪心+模拟,每次取剩下的个数最大的,在最大的里面如果有end,就先取end。
被priority_queueFunctor的奇葩规则坑了一会儿。
1 | #include "lucida" |
为什么这么简单的做法还是想不出来
先考虑暴力: 修改一个位置之后,从这个数字开始向后递推能否构成单调数列。每次是O(n)的,但是可以看出,做了很多无用功,因为每次转移的行为都是一样的,只要知道起点的值后面的值就都知道了。 考虑一下每个单调数列的来源,是i−1的单调数列加上一个满足不降且最小的数字。修改了一个位置之后,不能O(1)知道对于它后面的某个数字,它所转移过来的那个位置还是不是合法,也就是说,不能知道的传递路径是否被阻断,也不能知道是否还有别的路径。 但是可以发现,对于一段整体的数列,开头的两个数字是否合法就直接决定了后面所有点是否合法,即知道了的起始点,就能固定的路径。 这样就可以看出子结构了,进一步想一下,发现可以用线段树维护。
根据暴力的无用功,寻找问题子结构,寻找合法的状态和转移,从而套数据结构维护。
1 | #include "lucida" |
233居然没有想出单调队列的做法 觉得用线段树妥妥T飞,单调队列没法维护 再一次被教育了
f[i]=min{f[j]+(h[j]≤h[i])∣j+k≤i} 如果是f[j]加一个常数,显然维护一个f[]递增的单调队列 加的不是常数,但也可以看到加的数字非0即1。也就说,原本有f[u]<f[v],转移过来的DP值u至少不会比v大。 所以按照这个结论就可以维护了 DP值不相同,按照DP值从小到大排序 否则,按照高度从大到小排序
用类似贪心策略的分析来得到优化DP转移的结论
1 | #include "lucida" |
条件等价于选出一个子序列[l,r],使得对于 ∀i∈[l,r],count(p)[l,i]≥2i−l+1,count(p)[i,r]≥2r−i+1 然后化简 pref[i]−pref[l−1]≥2i−l+1,2pref[i]−i≥2pref[l−1]−(l−1) 对于后缀的情况同理 根据这个式子,可以O(nlogn)地求出以每个点为开头,向左\右延伸,最远的位置lnl,lnr。 得到了这个之后,问题就转化为 选出一对l,r,使得lnr(l)≥r,lnl(r)≤l 也可以O(nlogn)做
看到数据范围和时限,一开始觉得应该是一个很短的纯思路题。在题解的搜索页面上,看到了“树状数组”的字样,才往O(nlogn)上想。网上的解法都是2O(n)+O(nlogn),我的做法是3O(nlogn),感觉很虚。 果然写完之后T了。但是本着n方过百万,卡常出奇迹的心态,开始用gprof卡常。 最后把ST的写法改了一下,更好地利用了cache缓存,然后就A了。
卡常出奇迹 ST表要写cache友好的版本
1 | #include "lucida" |
为什么大家都一看这道题就是矩阵乘法。。为什么我就从来没听说过。。 不过用矩阵乘法的求法是很好理解的吧 如果边权为1,那就给邻接矩阵的a[0][u]赋值为1,表示以u为终点,走0步,有一种路径。 矩阵乘一下相当于走了一步,可以看一下矩阵乘法的式子,就是统计方案的DP。 这样的话,二分需要走多少步,然后矩阵快速幂,用方案数目跟K比较,最后可以得到答案。 然而会T。这样的话,就可以用类似倍增LCA的做法,处理出邻接矩阵的2的k次方,然后从大到小试乘,如果可以的话就加入答案。 边权不为1,那就把每个点拆成三个,表示边权为1,2,3的时候所到的点的分身。 因为拆了点,本来有些点是不会成为路径的终点的却可以成为路径的终点,这种方案不能算进去。 于是就在矩阵上乘上这个点的出度,相当于矩阵里是路径的倒数第二个点的方案数目,乘上一个出度才是完整的路径。 所以条件需要是<K,最后再Ans++
另外
矩阵乘法求路径数目?感觉很喵的样子
1 | #include "lucida" |
看到题,转化了一番,搞出了一个类似"选出不与别的边相交的边"之类的东西。。然后感觉非常复杂不会做 其实正解的思路也差不多,只是没有刻意去套那么鬼畜的模型。。
对于每种颜色,断点只能在相邻的两个珠子之间。 如果两条边能满足对于每种颜色,都在这种颜色相邻同一对珠子之间,就可以取。 所以! 对每种颜色,把相邻的两个珠子中间的片段都编上号。 这样,如果两条边的每种颜色的编号都相同,就可以取。这一步用哈希实现。 这样,对于一段哈希值相同的点,用乘法原理算方案数,用双指针算最优解。
坑点 如果在算哈希的时候碰上了每种颜色在链上的第一个珠子,需要把1..first,last..n都标上同一个号。 双指针的时候,右指针必须在解开始变差之前停下,否则可能影响左指针移动之后的计算。 so 要格外小心拆环的边界情况 双指针要如上地写 and 如果做题的时候套的模型太鬼畜导致不会处理,不一定是想法错了。。可以回归本质,用正常一些的模型试试
1 | #include "lucida" |
如果在树上的话,答案显然就是树的直径÷2。然后发现这道题的距离可以是实数,似乎不好设计状态DP,于是思考如何向树上转化。 基环树,处理的最常见的思路也就是拆环了(一个LCT好题)。那么把这个图的环枚举边拆掉,是不是直接求直径就行了? 然后观察一下,发现不管把点放在哪里,环上一定有一条边是不会被经过的。所以只要枚举断点拆环,得到的直径的最小值÷2就是答案了。 至于直径,一定是来源于环上挂着的子树的直径,或者是一个子树的一条链,经过环走到另一个子树。 第一种可以预处理 至于第二种,感觉做法挺有意思的。 设当前断点为p,其它所有点u到p的距离为d[u],每个子树中的最长链为ch[u],那么这个答案就是 把变量分离开,发现就是个max{d[v]+ch[v]+(−d[u]+ch[u])}只需要维护d[u]+ch[u]的最大值,和−d[u]+ch[u]的最小值即可! 所以建立两个线段树分别维护着两个量,那么当枚举的断边改变的时候该如何实时修改线段树的信息呢? 发现,如果硬是要维护到断点的距离的话,那么d[]就会这样变化: 0,d[2],d[3],d[4]..→d[n]+dist(n,1),0,d[3]−d[2],d[4]−d[2]→... 似乎比较麻烦的样子? 然而本来这个d[]就是个前缀和,需要的只是它两项的差值。只要维护相对大小正确就行了。于是修改断边,只需要修改一个点的值就可以,直接把它的d设置为它的前驱pred的d[pred]+dist(pred,this)即可。。虽然比较显然,但我还是感觉比较有意思。。
维护前缀和数组,可以只需要维护值的差正确 基环树先想拆环
1 | //Code by Lucida |
一看到这个式子,可以找循环节啊! 再一看互质……2333
一个等差数列膜掉一个数字之后再和另一个数字比较 假设小串出现在了大串的位置p∣p∈[1,n],那么有 ... 每个条件都可以转化为一个不等式,发现每个不等式可以看成对a(p−1)的限制,也就是对在大串中匹配上的第一个位置的数值的限制。 因为n,a互质,所以与每个位置p是一一对应的。 那就要解得到的这一堆不等式,找到满足要求的值的个数。 每个不等式形如 可能在膜意义下移项之后l>r,那也无所谓,只需要添加两个不等式即可。因为的值可以看成一个环,每个不等式限制了必须取环的一段。显然,l>r添加两个不等式是符合意义的。 发现区间交集并不好求,那就转化一下,求不合法位置的并集,用总个数减去即可。
区间操作不好做,考虑补集转化 膜的不等关系可以类比在环上区间的限制
1 | #include "lucida" |
我想出的优秀做法:
外层区间线段树,内层值域线段树,查询的时候线段树合并,在合并出的线段树上查询最大值与2r−l+1比较
想出来之后,一看Solved:700+?POI的题有这么多人做?有那么好写吗?
当我看到正解之后惊呆了
一切尽在代码中
给了这样的限制也许就可以得到有用的性质啊。。不分析限制直接暴力想对于所有的范围不可取啊。。
1 | #include "lucida" |
K非常大。。 由这个条件
先按权值从小到大排序,对于权值相同的,根据所用珠子集合的标号的字典序从小到大排序
可以启发得到一种想法:先找到第K大的权值,再构造相应字典序的解。
所以第一步需要找到第K大的权值,常用的思路是把选出的数字集合按照一些性质分类、扩展,用一个堆来维护(K个串)。对于这道题来说,有这样一种构造方法:
首先把整个序列排序
堆的每个节点维护一个数对<a,b>,表示和为a,编号的最大值为b。扩展的时候,扩展出<a+v[b+1],b+1>和<a−v[b]+v[b+1],b+1>
为什么这样就能不重不漏地取到前K大的权值和呢?为什么不能呢。。不会严格证明,但是可以感受到这样确实是可以不重不漏地取到的。。一个集合,根据倒数第二个点取或者没取,只有一个前驱;不停地递归下去,肯定会回到状态<v[1],1>
在计算第K大的权值和sum的同时,还要记录前K大的权值和中有多少个和第K大的权值和相同,设为c。那么就需要构造出权值和为sum,字典序第c大的集合。
让我万万想不到的是,构造的方法就是深搜。。每次找到最靠左的且小于当前总和的数字,减掉之后搜下去。。
想不明白复杂度?
如果能像这样先把权值搞掉只搜字典序,就大大简化了问题。。 字典序是有可能需要搜的。。。
1 | #include "lucida" |