可能是最后的垂死挣扎
上一篇写满了,又可以再开一篇了。
话说还有三周就要去退役了,真是快啊。
HZOI省选模拟66A 有限空间跳跃理论
考虑对一张\(DAG\)求拓扑序的过程,我们一次性删除当前所有入度为\(0\)的点,并把这个点集记下来,这样我们就得到一个拓扑序列。这个拓扑序列是一个点集序列,显然拓扑序列不同,\(DAG\)一定不同,于是我们考虑对这种特殊的点集拓扑序列计数。设\(dp_S\)表示集合\(S\)中点已经加入到了拓扑序列中的方案数,我们枚举一个集合\(T\)作为当前入度为\(0\)的点集,显然我们需要保证点集\(T\)内部没有边,否则\(T\)中的点不可能入度均为\(0\),之后\(dp_S\)向\(dp_{S\cup T}\)转移即可。不难发现这种情况会算重,原因在于\(T\)可能不是一个极大的入度为\(0\)的点集,我们可能将极大的入度为\(0\)点集分到了数次加入。考虑容斥,使得极大入度为\(0\)点集被计算的次数和为\(1\),设\(f_i\)表示\(i\)个点构成的点集的容斥系数,不难发现有\(\sum_{i=0}^{n-1}\binom{n}{i}f_{n-i}=1\),归纳一波发现\(f_i=(-1)^{i+1}\),于是有\(dp_S=\displaystyle \sum_{i\cup j=S,i\cap j=\varnothing}dp_i\times [\text{点集j内无边}](-1)^{|j|+1}\),不难发现这是一个子集卷积的形式,fwt优化即可,复杂度\(O(2^nn^2)\),代码。
HZOI省选模拟90A 洛希极限
设\(f_{i,j}\)表示以\((i,j)\)为终点的最长路,最最暴力的想法就是枚举一个\(i'<i,j'<j\)令\(f_{i',j'}\)向\(f_{i,j}\)转移,转移的时候还需要判断是否存在一个矩形同时包含\((i,j),(i',j')\)。不难发现只有当\(i-i'\)和\(j-j'\)均大于\(1\)时这个转移一定不优,所以只需要枚举和\((i-1,j-1)\)在同一行或同一列上的点即可。设\(left(i,j)\)表示使得\((i-1,x)\)和\((i,j)\)在同一个矩形里最小的\(x\),\(up(i,j)\)表示使得\((x,j-1)\)和\((i,j)\)在用一个矩形里最小的\(x\)。显然\(up(i,j)\)和\(left(i,j)\)是能从上面、左面向\((i,j)\)转移的最远点。不难发现\(left(i,j)\leq left(i,j+1),up(i,j)\leq up(i+1,j)\),于是这个dp可以用单调队列进行优化。考虑如何求出\(left(i,j)\)和\(up(i,j)\),显然可以扫描线,把一个矩形看成在上边界加入,在下边界删除,给线段树上每个节点开一个小根堆,标记永久化即可。求一行的\(left(i,j)\)和\(up(i,j)\)可以直接在线段树上dfs一遍,由于堆可以\(O(1)\)求出最大值,所以这个复杂度是\(O(m)\)的。整体复杂度大概是\(O(Q\log^2n+nm)\),由于\(\log n\)很小就直接跑过去了,代码。
「LibreOJ NOI Round #2」简单算术
一个常规想法就是枚举\(m\)的划分。如果\(\{b_i\}\)满足\(\sum b_i=m\)且\(\sum ib_i=k\),那么\(\{b_i\}\)对答案的贡献就是\(\displaystyle \prod_{i=0}^{n} \binom{m-\sum_{j=0}^{i-1}b_j}{b_i}a_i^{b_i}\)。可以将\(\displaystyle\binom{m-\sum_{j=0}^{i-1}b_j}{b_i}\)写成\(\displaystyle\binom{b_i+m-\sum_{j=0}^{i}b_j}{b_i}\),于是根据库默尔定理,这个组合数不为\(0\)当且仅当\(b_0+b_1+b_2+\dots+b_n\)在\(p\)进制下没有进位。考虑当\(p|m\)的约数时,\(m\)在\(p\)进制下最低位是\(0\),那么所有\(b_i\)在\(p\)进制下最低位也都得是\(0\),即所有\(b_i\)均满足\(p|b_i\)。根据Lucas定理,显然有:
我们要求的即\([x^k]F^m(x)\),根据上面的式子不难发现当\(p|m\)时\([x^k]F^m(x)\equiv [x^{k/p}]F^{m/p}(x)(\rm{mod}\ p)\)。当\(k/p\)不是整数时,\([x^k]F^m(x)=0\)。
于是可以将\(m\)写成\(ap+b\)的形式,于是有:
预处理\(F(x)\)的\(0,1,\dots,p-1\)此幂,直接暴力按照上面的式子迭代即可,复杂度\(O(n^2p^2+Tn^2\log_p m)\),代码。
HZOI省选模拟98D Radio Ga Ga
区间dp的做法比较显然,就是设\(f_{l,r}\)表示考虑了区间\([l,r]\)的最大收益,由于知道了区间长度就知道了操作次数,所以转移非常简单,但是\(n\leq 10^9\)显然白给。考虑换个dp方式,设\(dp_{i,0/1}\)表示在第\(k_i\)此操作恰好扩展到\(x_i\),此时\(x_i\)为左/右端点的最大收益,显然这个时候的区间就是\([x_i,x_i+k_i-1]\)或是\([x_i-k_i+1,x_i]\),转移的话就考虑枚举上一次取到的收益是在\(k_j\)时刻,能转移过来的条件是\(k_j\)时刻的区间被当前区间包含,且\(k_j\)时刻的区间端点不能是\(x_i\)。由于必须要保证\(k_j<k_i\),所以不难发现转移构成了一个DAG,问题转化为在这个DAG上求一条点权最长路。由于有多组询问,所以考虑倒着进行dp即可。暴力转移的复杂度是\(O(m^2)\),不难想到搞个二维数据结构来求矩形max优化转移,复杂度是\(O(m\log m^2)\)或\(O(m\sqrt{m})\),为了复习kdt就写了一发。这种求最值信息的kdt加上剪枝之后能跑过\(3\times 10^5\)级别的数据。
正解其实比较显然,考虑将这\(2m\)个区间拿下来按照左端点从小到大排序,直接扫过去,在线段树里插入右端点即可。显然这样能保证包含关系,又由于存在包含关系的两个区间\([l_i,r_i],[l_j,r_j]\)一定满足\(r_j-l_j+1\leq r_i-l_i+1\),区间长度即当前操作次数,所以有\(k_j\leq k_i\),这样转移的两个条件就都满足了。复杂度\(O(n\log n)\),但懒得写了,后面的代码是kdt的80pts,代码。
【NOI2019】回家路线
复习一下斜率优化,为了写凸包二分的板子就没写单调队列。记得这个题我去年还只会暴力来着,所以这是否说明我可能比去年强了一点?代码。
【UER #6】寻找罪犯
发现如果只对每个人建点的话不是很好做,于是考虑把每个人的供词也建出点来;考虑2-sat,人与供词以及供词与人之间的连边比较显然,问题是如何利用2-sat来实现每个犯人最多只能说一句假话的限制。一个暴力的想法是对于一条供词\((x,y)\),另这个供词为假向\(x\)说的其他供词为真连边;即如果\((x,y)\)这条供词为假,那么就能推出\(x\)说的其他供词为真,这样连边的复杂度是\(O(m^2)\)的。显然可以对于每一个人将其说的供词列出来,搞一个前后缀优化建图,这样复杂度就是线性了。代码。
ZR省选十连测Day2C LCA on Tree
设\(G(x)=(sz_x+1)W_x\),\(sz_x\)表示\(x\)这棵子树的大小,\(W_x\)表示子树\(x\)的权值和;通过观察不难发现\(F(x)=G(x)-\sum_{v\in son(x)}G(v)\)。考虑这个东西可以拆成\(G(x)\)本身的贡献、\(x\)重儿子的贡献、轻儿子的贡献;由于重儿子只有一个,所以维护一下子树和就能快速求出\(x\)本身和重儿子的贡献;至于轻儿子的贡献,我们可以暴力跳重链。由于这个修改操作比较别致,所以需要按照bfs序来维护一些线段树、树状数组来支持对儿子和孙子进行修改,推推式子就没了。代码。
多校联考104B 蓝超巨星
设\(L\)是最小的满足\(aL\equiv 0(\rm mod\ n)\)的正整数,显然\(L\)就是循环左移的最小循环节。考虑枚举答案\(\rm{mod}\ L\)的值,这样我们就能知道循环左移了多少位。
对于\(S\)中的每一种字符我们记录其第一次出现位置,就可以知道在循环左移当前位数下,每一种字符应该变成什么字符。考虑字符\(c\)想要变成\(c'\),首先得满足\(c\)和\(c'\)在同一个置换环里,同时还得保证出现位置是相同的。搞一个\(hash\),对于每一种字符记录其相邻出现位置的差,就能快速判断了。之后我们就得到了一些形如\(ans\equiv b_i(\rm{mod}\ a_i)\)的同余方程,直接excrt合并下来就好了。时间复杂度是\(O(|\sum|n\log n)\),代码。
多校联考103B 农民(farmer)
对于一个点\(x\),如果\(y\)是\(x\)的祖先且\(x\)在\(y\)的左子树中,那么称\(y\)是关于\(x\)的左偏点;如果\(x\)在\(y\)的右子树中,就称\(y\)是关于\(x\)的右偏点。发现如果\(x\)能被访问到的话,必须要满足\(x\)小于所有左偏点的点权,大于所有右偏点的点权,于是我们只关心左偏点点权最小值和右偏点点权最大值。考虑树链剖分,发现在没有二操作的时候每一条重链的偏移关系是固定的,于是可以直接用线段树来维护;跳轻边的时候直接判断即可。显然只会跳\(\log n\)次轻边,所以复杂度是正确的。考虑二操作的意义就是使得一个子树内部的偏移关系取反,即左偏变成右偏,右偏变成左偏。我们对于左偏点的右偏点均维护最大值和最小值,在线段树上打一个翻转标记交换最大值和最小值即可。
复杂度是\(O(m\log^2 n)\),代码。
多校联考101A 石子游戏
转化一下题意,就是找到一个尽量小的集合\(\{p_i\}\)使得\(\displaystyle \bigoplus_{i=1}^m a_{p_i}=\displaystyle \bigoplus_{i=1}^n a_{i}\),一个显然的形式是集合大小\(m\)不会超过\(\log w\)。从线性基的角度来考虑,至多\(\log w\)个可以插入线性基的元素即可表示出\([0,w)\)之间的所有数。所以我们暴力做\(\log w\)遍fwt,每做完一次ifwt回来看一下那个点的点值是否为\(0\)。fwt可以直接转成点值对位相乘,求一个点ifwt回来的点值可以直接扫一遍。于是复杂度是\(O(w\log w)\),代码。
多校联考100A 小B的棋盘
这个对称中心反映的其实就是这些点两两匹配的情况,即对称中心一定是一对匹配的点形成的线段的中点。于是如果\(n>k\),那么至少会有一对匹配的点来自于给定的\(n\)个点中,这样的话可能的对称中心一定是某一对匹配点的中点;如果\(n\leq k\),那么对称中心就会有无限多个了,输出-1即可。
一个最暴力的想法是如果\((x,y)\)能成为对称中心的话,那么至少有\(\frac{n-k}{2}\)对点形成的线段的中点是\((x,y)\),于是可以暴力求出所有点对的中点,之后排序扫一遍即可,复杂度是\(O(n^2\log n)\)。考虑将所有点按照横坐标从小到大排序,那么如果线段\(AB(A<B)\)和线段\(CD(C<D)\)的中点相同,必然要满足\(A<C<D<B\),而我们需要匹配出至少\(\frac{n-k}{2}\)条线段,所以需要至少\(n-k\)个点;我们排序之后枚举这样的点对\(i,j\),显然必须有\(j-i+1\geq n-k\),即\(i,j\)能匹配的话他们之间就必须有至少\(n-k\)个点,所以这样的点对不会超过\(k^2\)个,暴力这样的点对,利用上面那个单调的性质双指针匹配即可,复杂度是\(O(nk^2)\),代码。
多校联考99B 西行寺无余涅槃
一个序列\(A\)做异或fwt的本质其实是这样的\({\rm FWT}(A)_i=\displaystyle \sum_{j=0}^{len-1}(-1)^{{\rm popcount}(i\& j)}A_j\)。
还是丢一份别人的题解吧,复杂度是\(O(2^{k-1}k(n+2^m))\),代码。
多校联考97B 幻化成风
发现要求\(\{b_i\}\)中的数两两不同还是很难做的,于是考虑容斥,一个暴力的想法是直接枚举子集划分,使得一个集合里的数强行相等。这个容斥系数之前一直不会来着,去vuq里问了才知道是\(\displaystyle \prod_{}(-1)^{size-1}(size-1)!\),\(size\)即连通块的大小。
被划分到了同一个子集里的数需要强制相等,于是幂次可以直接相加,我们要求的即\(\displaystyle \prod_{i=1}^{m'}x_i^{c_i}=n!\)的方案数,这里不要求\(x_i\)互不相等。不难想到\(n!\)可以写成\(\displaystyle \prod p_i^{\omega_i}\),于是我们只需要对每一个\(\omega_i\)求方程\(\displaystyle \sum_{j=1}^{m'}c_jx_j=\omega_i\)的方案数之后乘起来即可。从生成函数的角度考虑,发现这其实就是在求\(\displaystyle \prod_{i=1}^{m'}\frac{1}{1-x^{c_i}}\),直接暴力求出分母暴力多项式求逆即可,由于分母的最高次幂不会超过\(m\),所以复杂度其实是\(O(nm)\)的。由于我们还需要暴力子集划分,所以复杂度是\(O({\rm Bell}(m)nm)\)的。
注意到我们其实并不关心子集划分的情况,我们只关心\(\{c_i\}\)长什么样子。于是我们可以爆搜\(\displaystyle \sum a_i\)的整数划分,之后用一个状压dp来计算这种划分方案的系数。这个状态dp大概就是\(dp_{x,S}\)表示已经凑出了前\(x-1\)个集合,当前的状态为\(S\)的方案数,这里的\(S\)并不是一个二进制状态,而是记录每一种\(a_i\)还剩下多少个。由于\(\displaystyle \sum a_i,m\leq 30\),所以这个状态数非常小,直接暴力记搜转移即可。
但是这样还是有\(10^3\)种整数划分是有用的,于是还是T了,代码。
多校联考94A a
考虑贡献的式子\(\frac{(A_i-A_j)B_iB_j}{2A_iA_j}=\frac{B_iB_j}{2}(\frac{1}{A_j}-\frac{1}{A_i})=\frac{1}{2}(B_i\times \frac{B_j}{A_j}-B_j\times \frac{B_i}{A_i})\),发现这个贡献可以看成\((B_i,\frac{B_i}{A_i})\)叉\((B_j,\frac{B_j}{A_j})\)和原点形成的有向三角形面积。题目中要求的路径满足\(B_i\),即点的横坐标是单峰的;贡献和尽量大且还需要回到起点。发现这其实就是描述了一个凸包,于是求一下凸包面积即可。复杂度\(O(n\log n)\)。
多校联考96B 仙人掌
显然一个点的点权=\(\text{儿子被操作次数}+\text{父亲被操作次数}\);如果操作\(x\)这个点,那么只有\(x\)的父亲这一个点的\(\text{儿子被操作次数}\)改变;而对于\(x\)的所有儿子\(\text{父亲被操作次数}\)都增加了\(1\)。
显然父亲的点权我们可以直接\(O(1)\)得到,难点在于处理儿子的点权异或值。考虑在\(x\)处将\(x\)的所有儿子都存下来,那么我们需要一个数据结构,支持整体加一、查询整体异或值;同时一个儿子的点权还可能因为其子树内部的点影响而改变,所以还需要插入、删除。
考虑\(x\)变成\(x+1\)在二进制下的变化,发现就是从低位到高位找到第一个\(0\),之后把后面的\(1\)都变成\(0\)即可;可以用一棵trie树来维护,插入的时候按照从低位到高位的顺序插入,整体加一时只需要交换左右儿子,之后递归到左子树(即原来的右子树,在这一位上是\(1\))即可,同时维护子树异或值就能查询全局异或值了。
时间复杂度是\(O((n+m)\log n)\),代码。
[NOI2017]整数
做法非常显然,把\(a\)分解成二进制,显然只会影响\(b\)往前的\(30\)位。把这\(30\)位拿出来,做一个二进制下的竖式加法/减法即可。最后可能还需要处理一个进位/借位的问题,需要找到前面的第一个\(0/1\),同时把一段\(1/0\)取反。不难发现这些操作线段树都能进行,时间复杂度是\(O(30n\log n)\)。有了这个做法不难想到将\(30\)个二进制位压在一起,就能做到\(O(n\log n)\)了。
但是压位太难写了,于是考虑换一个做法。不压位线段树的复杂度带一个\(30\)的原因是处理\(b\)往前的\(30\)个数位时都需要去线段树上单点查/改,考虑找一个数据结构能快速实现把这\(30\)位提取出来,并快速地把修改之后的\(30\)个数位修改回去。发现平衡树可以轻松胜任这个工作,用两棵平衡树分别维护\(0/1\)出现的未知,找这\(30\)个数位就是一个区间提取,将修改后的\(30\)个数位插入可以直接建出二叉树,将这个二叉树插入进去。于是单次的复杂度就是\(O(30+\log n)\),整体复杂度是\(O(n\log n+30n)\)。
但是由于splay常数实在是太大了,所以loj也跑不过去,代码。
CF547D Mike and Fish
对于点\((i,j)\)我们视为一条从\(i\)连向\(j\)的无向边;现在我们要给所有无向边定一个方向,如果是\(i\rightarrow j\),那么就将\((i,j)\)涂成红色;如果是\(j\rightarrow i\),那么就将\((i,j)\)涂成蓝色。发现只需要保证给边定向之后每个点均满足\(|\text{入度}-\text{出度}|\leq 1\),就等价于使得每行每列两种颜色最多相差\(1\)。
一个特殊情况是所有点的度数都是偶数,那么这张图就是一张欧拉图,我们dfs一遍找一条欧拉回路,沿着欧拉回路给每条边定向,这样每个点均满足入度=出度,符合要求;如果存在奇度点,那么奇度点的个数肯定是偶数个,令所有奇度点向一个虚点连边,这样虚点的度数也肯定是偶数。在这张图上找欧拉回路并给边定向即可,给边定向后将虚点和所有与虚点相连的边删去,发现对于奇度点均满足\(|\text{入度}-\text{出度}|=1\),其余点均满足\(\text{入度}=\text{出度}\)。
其实还胡了一个用lct维护的做法,但是太憨憨了,就不写了。代码。
[JLOI2016]成绩比较
不想写题解了,憨憨题,大概就是容斥一波之后胡乱推推式子,最后得到一个需要自然数幂次方和的东西,大力拉格朗日即可。复杂度\(O(m^2n)\),代码。
[APIO2016]划艇
首先需要将区间离散化成若干个左闭右开的区间,发现给每个学校选一个值并且递增是非常困难的,因为这个值是\(10^9\)级别。但是我们找出一个递增的序列再分给每个学校却是非常可行的,这样我们只需要保证学校的编号递增,而学校的编号只有\(500\)级别。
设\(dp_{i,j}\)表示对于离散化后的前\(i\)个区间,最大的学校编号为\(j\)的方案数。转移的话我们就在当前这段区间里找一个更大的学校,大概可以写成这样:
\(len_i\)表示第\(i\)段区间的长度,\(S_i[k+1,j-1]\)表示在第\(i\)段区间中有多个可以填的学校编号\(x\)满足\(k+1\leq x\leq j-1\)。大概就是枚举一下前\(i-1\)个区间中的最大编号\(k\),那么在区间\(i\)中我们选择的学校编号都需要大于\(k\),最大的编号是\(j\),那么我们就从\(S_i[k+1,j-1]\)个学校里选\(p\)个出来,让这\(p\)个学校和编号为\(j\)的学校构成一段上升序列即可。
直接暴力转移的复杂度是\(O(n^4)\)。不难注意到最后的那个\(\sum\)只跟\(S_i[k+1,j-1]\)有关,而\(S_i[k+1,j-1]\)不会超过\(n\),于是我们预处理\(S(z)=\sum_{p=0}^z\binom{len_i}{p+1}\binom{z}{p}\)就能做到快速转移了,时间复杂度为\(O(n^3)\),代码。
CF527E Data Center Drama
- 无向图存在欧拉回路的条件:所有点度数均为偶数。
- 无向图存在欧拉路径的条件:至多两个点度数为奇数。
- 有向图存在欧拉回路的条件:所有点均满足入度=出度。
- 有向图存在欧拉路径的条件:一个点满足出度-入度=1,作为起点;一个点满足入度-出度=1,作为终点;其余点均满足入度=出度。
这道题要求所有点的入度、出度均为偶数,那么就首先得保证每个点的度数均为偶数。于是先把图构造成一张欧拉图,把奇度点拿出来,每两个点连一条边即可。进一步注意到一条边会使一个点的入度+1,一个点的出度+1,即所有点的入度和+1,出度和+1;如果有奇数条边,那么所有点的入度和和出度和也都会是奇数,不可能使得每个点的入度、出度均为偶数;所以存在偶数条边是一个必要不充分条件,所以如果构造成欧拉图之后有奇数条边,那么就随便钦定一个点给它加一条自环即可。
之后不会了,观察题解可以发现,我们跑一个欧拉回路,隔一条边换一个方向即可,代码。
CF627E Orchestra
一个经典的做法枚举左边界、右边界,之后就得到了一个长度为\(r\)的序列,并将问题转化为求\(\geq k\)的子区间个数,用双指针扫一下即可,复杂度是\(O(c^2r)\);我们注意到\(k\)非常小,于是考虑利用一下这个性质。还是枚举左边界,之后把所有的点都加进去,每次删除最右边的点。我们用双向链表维护序列中大于等于\(0\)的元素,设\(l_i\)表示\(i\)前面第一个满足到\(i\)的区间和\(\geq k\)的位置,一个显然的事情是我们至多在链表上往前跳\(k\)次就能找到\(l_i\)。于是删除一个点,即使得序列中某个元素减一,至多只会影响其后面\(k\)个点\(l_i\)的变化;且由于只是减一,所以对于减一后不合法的\(l_i\)最多在链表往前跳一步就是新的合法的\(l_i\)。于是在链表中删除一个点的复杂度是\(O(k)\)的。
一个点最多只会被删\(c\)次,所以复杂度是\(O(c^2+nck)\),代码。
SDOI2013淘金
一个显然的事实是行列是独立的,设\(g(i)\)表示\(1\)到\(N\)中满足\(f(x)=i\)的\(x\)有多少个,那么\((i,j)\)上的金子数量就是\(Au(i,j)=g(i)\times g(j)\)。写个爆搜发现合法的数位积大概只有八千种左右,于是可以搞一个数位\(dp\)把所有的\(g\)都算出来。\(dp(i,j,0/1,0/1)\)表示从高到低填了\(i\)位,数位积为\(j\),是否卡上界,是否有前导\(0\)。
求出\(g\)之后要求出前\(k\)大的\(Au(i,j)\)的和,将\(g\)排序之后开个堆即可。代码。
SDOI2019世界地图
考虑现在有了前缀mst以及后缀mst,我们怎么合并得到新的mst;一个直观的想法是用类似lct维护mst的做法,对于新加入的一条边,看一下加入后是否成环;如果不成环就直接加入,否则就断掉环上边权最大的一条边。我们注意到前后缀mst合并的时候新的边都形如\((1,i)-(m,i)\),即新加入的边都是连在第一排和第\(m\)排之间。考虑上面lct维护的过程,发现断掉的边一定在\((1,i)\)到\((1,j)\)的路径上,否则就在\((m,i)\)到\((m,j)\)的路径上,这样的话就只有第一排和第\(m\)排的点是有用的,所以如果知道第一排和第\(m\)排和虚树的话,我们把虚树上的边和新加入的边拿下来做一个\(kruskal\)就能得到合并后的mst了。
考虑对于每一个前缀mst求第一排点形成的虚树,后缀同理。对于前缀\(i-1\),我们维护第一排和第\(i-1\)排形成的虚树。只需要把第\(i\)排的点和边拿下来,像回答询问一样做一个合并就能得到前\(i\)排的mst了,对于新的mst我们还是求出第一排和第\(i\)排形成的虚树,继续往后推即可。时间复杂度是\(O(n(q+m)\log n)\)。
CF582D Number of Binominal Coefficients
库默尔定理:\(\binom{a+b}{a}\)中\(p\)的幂次等于\(a+b\)在\(p\)进制下的进位数。证明的话看这位老哥的吧。
\(p^\alpha|\binom{n}{k}\)等价于\(\binom{n}{n-k}\)的\(p\)幂次数\(\geq \alpha\)。考虑数位\(dp\),把\(A\)转化为\(p\)进制(直接暴力转的复杂度是\(O(\log_pA\log A)\)),搞一个\(f(i,j,0/1,0/1)\)表示从高到低填到了第\(i\)位,\(a+b\)的进位次数为\(j\),是否卡上界,是否需要下一位的进位的方案数。转移大力分类讨论即可。
CF573E Bear and Bowling
显然非负数是要全部选上的,考虑对负数的选择做一个\(dp\)。设\(dp_{i,j}\)表示前\(i\)个数里选了\(j\)个负数,转移非常简单,为\(dp_{i,j}=\max\{dp_{i-1,j},dp_{i-1,j-1}+a_i\times j+rsum\}\),其中\(rsum\)表示\(i\)后面非负数的和。
写个暴力把dp数组打出来,可以发现dp数组是单增的,且差分之后是单减的,且对于每一个\(i\),必定存在一个\(k\)满足,\(\forall j\in [0,k],dp_{i,j}=dp_{i-1,j}\)且\(\forall j\in (k,i],dp_{i,j}=dp_{i-1,j-1}+a_i\times j+rsum\),即存在一个分界点使得前面都是第一种转移,后面都是第二种转移。感性理解一下就是负数选得越多,贡献增长就越慢,于是差分数组单减;而只有当\(dp_{i-1,j}-dp_{i-1,j-1}\)即差分后的结果小于\(a_i\times j+rsum\)的时候才会进行第二种转移,所以大概只有后缀进行了转移。瞎口胡的,感觉好假啊
于是可以用数据结构维护这个\(dp\),考虑维护差分数组,设\(c_j=dp_{i-1,j}-dp_{i-1,j-1}\);我们二分找出两种转移的分界点\(k\),则\(c'_k=a_i\times k+rsum\),\(\forall j\in (k,i],c'_i=c_{i-1}+a_i\),发现\(k\)后面相当于整体往后移了一位之后进行了一次区间加,而\(c_k\)的修改就是一个单点插入;于是我们可以用splay来维护差分数组即可。
复杂度的瓶颈在于二分找分界点,于是复杂度是\(O(n\log^2 n)\)的,代码。
UOJ#523 【美团杯2020】半前缀计数
对于两个半前缀\([1,i_1]+[j_1,k_1],[1,i_2]+[j_2,k_2]\),不妨假设\(i_1<i_2\)。那么这两个半前缀本质相同当且仅当\([j_1,k_1]=[i_1+1,i_2]+[j_2,k_2]\),即后缀\(j_1\)和后缀\(i_1+1\)有一段长度为\(i_2-i_1\)的LCP。由此我们可以得出,当后缀\(j_1\)和后缀\(i_1+1\)的LCP长度大于\(1\)时,\([1,i_1]+[j_1,k_1]\)这个半前缀一定能被一个满足\(i_2>i_1\)的前缀形成的半前缀表示出来。
于是考虑倒着枚举每一个前缀,对于前缀\(i\),我们考虑在\([i+1,n]\)中找本质不同的,和后缀\(i+1\)的LCP长度为\(0\)的子串,这样的子串拼上\([1,i]\)后一定和那些更大的前缀形成的半前缀本质不同;倒着枚举前缀的时候用SAM来构建后缀树,和后缀\(i+1\)LCP长度为\(0\)的子串在后缀树上与\(i+1\)的LCA肯定是根节点,于是我们对于每个节点维护其来自根节点的哪个儿子,对于根节点的每个儿子维护其子树内本质不同的子串个数即可。
时间复杂度\(O(n)\),代码。
LGP3476 [POI2008]TRO-Triangles
\(O(n^3)\)暴力非常显然,枚举三个点海伦公式爆算即可。考虑只枚举一个点,钦定这个点为原点,其余点按照到这个点的极角序排序。众所周知,叉积可以算三角形面积,于是快乐双指针,对于每个点扫出最远的叉积为正的点,分别维护横纵坐标之和,就可以叉积快速算了。
时间复杂度\(O(n^2\log n)\),代码。
LGP5769 [JSOI2016]飞机调度
把每一条路径看成一个点,如果飞机能从\(i\)路径切换到\(j\)路径我们就连一条\(i\)到\(j\)的边。不难发现由于时间是不断增加的,所以这样连边得到的图应该是一张\(\rm DAG\),直接跑\(\rm DAG\)最小路径覆盖即可。代码。