JOI2020

这一切还要从一只蝙蝠说起


只不过是长的领带

先考虑\(n\)个人和\(n\)个领带的匹配,对于权值最大的领带,显然和权值最大的人匹配更优,因为如果拿权值最大领带和别的人匹配,然后权值最大的人和小点的领带匹配,这样的贡献显然不会更少,所以最优匹配其实就是分别对两者排序后依次匹配

现在要求去掉每一个领带后剩下领带匹配的权值,我们先把领带按大小排序,然后对于去掉第\(i\)大的领带的情况,更小的\(i-1\)个领带会依次跟权值最小的\(i-1\)个人匹配,更大的\(n+1-i\)个会依次和后面\(n+1-i\)个人匹配,所以预处理前\(i\)个领带和人的匹配最大值和后\(i\)个领带和人的匹配最大值就可以做到每次询问\(O(1)\)

JJOOII2

这里可以枚举删掉的前缀长度,然后相当于要选一个接着这个前缀的最短子段,满足依次有\(k\)\(J\),\(k\)\(O\),\(k\)\(I\),我们可以维护三个指针,分别表示含有\(k\)\(J\)子段的最小右端点,有\(k\)\(J,\)\(k\)\(O\)子段的最小右端点,有\(k\)\(J\),\(k\)\(O\),\(k\)\(I\)的最小右端点,并且预处理每种字符出现次数前缀和就可以一步步往后移指针.注意到每次移动前缀的时候这三个指针都是单调向后移动,复杂度\(O(n)\)

集邮比赛3

断环成链后有个显然的dp是\(f_{i,j,k,0/1}\)表示当前遍历过了\([i,j]\)里面的点,所用时间为\(k\),当前在\(i\)或者是\(j\),的最大集邮数,转移可以做到\(O(1)\).但是所有时间数值比较大,同时可以发现邮票总数为\(O(n)\)级别,所以把状态的\(k\)改为当前集邮数,把状态的值改为用的最少时间即可,同样可以\(O(1)\)转移

奥运公交

\(\small\text{这个菜字就写在我的脸上...}\)

先建出以\(1\)为根,以\(n\)为根的最短路树,然后在反图上再建以\(1\)为根,以\(n\)为根的最短路树,这个时候再枚举一条要翻的边,如果这条边没有在任何一棵最短路树里出现,那么之前的最短路还是可以用的,我们就只要讨论新的最短路是否经过翻过来的边,再用最短路树的信息更新答案即可;否则,由于这样的边数量是\(O(n)\)级别的,可以删掉这条边后跑最短路,这里复杂度为\(O(n(n^2+m))=O(n^3+nm)\)

火灾

先以位置为横轴,时刻为数轴,写出一个矩阵,每一位分别对应每个时刻每个位置的权值.然后考虑每个位置对这里权值变化的贡献

对于一个位置\(i\),它可以在之后的一段时间内覆盖后面一段权值更小的位置,直到碰到权值\(\ge\)它的位置后停止,设这个停止位置为\(nt_i\)(这个位置显然是\(i\)后面第一个权值不小于\(i\)的位置),那么在\(t\in[1,nt_i-i)\)时刻第\(i+t\)位置的权值会被覆盖为\(s_i\).我们把上述的覆盖看做是先减去原来的值,然后加上现在的值,那么也就是\(i\)号位会在\(t\)时刻给\(i+t\)号位加上\(s_i\)

然后考虑减权值部分.一个位置的权值显然是被往前走第一个权值更大的位置覆盖,记此位置为\(ft_i\),那么从\(ft_i\)上的权值覆盖到\(i\)开始,之后一段时间还会去依次覆盖后面的一些位置,直到到\(nt_i\)这一位没有\(i\)覆盖的贡献.形式化的讲,就是在\(t\in[i-ft_i,nt_i-ft_i)\)时刻,第\(i+t-(i-ft_i)\)位会减去原有权值,即\(s_i\)

可以发现上述的贡献总数是在\(O(n)\)级别的.对于一个询问\((t,l,r)\),先套路的拆成是\((t,1,r)-(t,1,l-1)\).然后现在一个\((t,1,i)\)就是在纵座标为\(t\)的一排里前\(i\)位的和.然后我们把一开始的矩阵的相邻两排前后差分,就可以发现对于前面讨论到的一种贡献在差分后的矩阵上是一段斜着的线段.另外现在的每个询问也就是对应区间的初值加一个前缀的矩阵的和

对于一个斜的线段,起始点和终止点分别为\((x,y),(x',y')\),我们可以看成是一个正贡献点\((x,y)\)和一个负贡献点\((x'+1,y'+1)\)往右上方的贡献

对一个贡献点\((x,y)\),设其权值为\(w\),对询问\((t,1,i)(i\ge x,t\ge y)\)的贡献为\(w*\min(t-y+1,i-x+1)\).那么可以发现取出\((i,t)\)点所在的左下-右上对角线后,取到\(t-y+1\)这一部分的贡献点全在对角线上方,反之全在对角线下方.所以对于对角线进行扫描线,先把所有贡献点放在对角线下方的贡献里,然后扫到当前对角线后把线上的贡献点贡献一到对角线上方的贡献里;对于端点经过当前对角线的询问,只要查询前缀\(i\)的下方贡献以及前缀\(t\)的上方贡献即可.我们把贡献记在树状数组里,如果树状数组上第\(i\)为权值为\(w_i\),那么对询问前缀\(n\)的贡献为\((n-i+1)w_i\),把贡献拆为\((n+1)w_i\)\(-iw_i\)两部分,那么树状数组分别维护\(\sum w_i,\sum iw_i\)即可

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章