不定时更新(遇到一道好题就会更新)
转换法
有一些题的操作难以实现,这时可以考虑转换法。
- P7735 [NOI2021] 轻重边
本题的修改操作较难,故让它第i次修改只是把两点路径上的点染成i颜色。
可以发现,若一条边两点颜色相同,则该边为重边,否则为轻边。
那么询问就变成路径上有几个不同颜色段,用树链剖分+线段树即可,O(nlog2n)。
- CF1334G Substring Search
si与tj匹配需要si=tj或si=p(tj)
转化为(si−tj)(si−p(tj))=0,即s2i−sitj−sip(tj)+tjp(tj)=0
整个字符串匹配成功可以转化为|S|∑i=1(s2i−sitj+i−1−sip(tj+i−1)+tj+i−1p(tj+i−1))2=0
|S|∑i=1(s2i−si(tj+i−1+p(tj+i−1))+tj+i−1p(tj+i−1))2=|S|∑i=1s4i−2(tj+i−1+p(tj+i−1))s3i+((tj+i−1+p(tj+i−1))2+2(tj+i−1p(tj+i−1))s2i−2(tj+i−1+p(tj+i−1))tj+i−1p(tj+i−1)si+(tj+i−1p(tj+i−1))2
将s翻转后即可转化为卷积形式,用NTT可以达到O(nlogn)
- P3204 [HNOI2010]公交线路
可以转换为:
- 在长度为n的序列里填数,可以填[1,k]内的数
- 前k个满足第i个填的是i
- 最后k个为[1,k]的排列
- 每个长度为p的区间内要包含[1,k]中的所有数
fi,S表示前i−1个车站已经填完,S为[i,i+P−1]是否填过的状态 时的方案数
fi,S+=fi−1,S′(⌊S′2⌋⊆S,S′&1=1,popcount(S)=popcount(S′)=k)
为了保证每个都填,S′&1必须为1,即第i−1位一定要填
为了保证满足限制4,我们强制转移时[i−1,i+P−2]和[i,i+P−1]都满足[1,k]各填了一个,所以popcount(S)=popcount(S′)=k
转移的含义:让S中新填的数和第i−1位填的数一样。
初始状态为f1,2k−1,答案状态为fn−k+1,2k−1
可以发现状态数为Ck−1p−1≤C59=126,可以使用矩阵快速幂优化,时间复杂度O((Ck−1p−1)3logn)
- 某题
非空正整数数列{am}满足∀k,ak≥2,h(n)表示满足m∏k=1am=n的序列个数,求n∑k=2k×h(k),2≤n≤3.5×107
h(i)很难算,所以考虑转换答案:
k×h(k)就是乘积为k的序列的乘积之和,所以答案就是乘积在[2,n]范围内的序列的乘积之和。
设fi表示乘积在[2,i]的序列的乘积之和,则有转移方程:
fi=1+i∑d=2d×f⌊id⌋(算上空序列)
答案为fn−1
因为⌊⌊na⌋b⌋=⌊nab⌋,所以所有要算的状态都可以表示为⌊nx⌋的形式
用整除分块优化可以达到时间复杂度O(n0.75),空间复杂度O(√n)
- [ARC110E] Shorten ABC
将 ABC 分别替换为 123,可以发现一次操作后整个序列的异或和是不变的。
然后由原串 S 得到的新串 T 有个性质:T 的每个字符都对应 S 串的一段区间。
一个区间可以操作成一个字符,要满足下列两个条件之一:
- 长度为一
- 异或和不为零且包含的字符不全部相同
为了防止算重,每次只选长度最短的区间计算即可。
设 fi 表示 [1,i] 能得到的序列总数,gi,j 表示 i 为左端点,对应字符为 j 的最短区间的长度。
gi,j={1j=aigi+1,j⊕ai+1j≠ai
fi→fi+gi+1,j
建图优化
当图的边数过多时,用各种方法减少边的数量。
- P6378 [PA2010] Riddle
明显的2-SAT问题,但是每个大小为w的部分要建w2条边,于是用前缀和建图。
原本第i个点有:是/非关键点两种(记为ai,1和ai,0),都要维护一个前缀(记为si,1和si,0),再按2-SAT建边。
之后就能把边的个数降为线性。
- P6822 [PA2012]Tax
较大值条件难以处理,考虑重新建图。
可以把点看做边,边看做点。但是边又变成n2级别。
之后把原图中的同一点的出边(单向)按边权排序,并且前缀和建图。
排序后第i条原单向边为ui权值为wi,其反边为vi
则ui→ui+1,边权为wi+1−wi
ui→ui+1,边权为0
vi→ui,边权为wi
这样就实现了较大值并且少了边,之后新开源点和汇点处理起始就可以跑最短路了。
- P3783 [SDOI2017]天才黑客
题意:
一条有向路径的长度为这条路径上每个边的边权之和+按照路径的顺序将这些边上的字符串排成一列,相邻两个串的lcp长度之和。求有向图中1号点到其他点的最短路。
和P6822 [PA2012]Tax 很像的一道题,由于lcp长度长度难以处理,所以把点看做边,边看做点。
例如ei:awi,si−−→b,ej:bwj,sj−−−→c
就连成:aiwi−→bilcp(si,sj)−−−−−→ajwj−→bj
可以发现中间的边是O(m2)的,考虑前缀和优化。
在字典树上有:(用F[l,r]表示dfn[r]mink=dfn[l]dep[k])
lcp(si,sj)=dep[lca(si,sj)]=F[si,sj]
假设要连出边的点的集合为b1,2,⋯,k1,要连入点的集合为a1,2,⋯,k2(都按字典树的dfn排序了)
那么我们只要bi0→bi+1,ci0→ci+1,bilcp(bi,chi)−−−−−→chi,bgilcp(gi,ci)−−−−−→ci就能实现让bilcp(bi,cj)−−−−−→cj(j≥hi)了。
其中hi指最小的j使得dfn[cj]≥dfn[bi],gi指最大的j使得dfn[bj]≤dfn[ci]
原理是这样的:F[bi,cj](j≥hi)=min{bghmink=iF[bk,chk],jmink=hiF[bgk,ck]}(这些区间都包含在[bi,cj]内)
这样只向dfn更大的连了边,所以要把ei拆成四个点ai,0/1,bi,0/1并连四条边:ai,jwi,si−−→bi,k(j,k∈{0,1}),之后对于bi,0连ai,0的连dfn更大的,对于bi,1连ai,1的连dfn更小的就行了。
新图点的个数和边的个数都是差不多4n,时间复杂度为O(nlogn)。
- P5471 [NOI2019] 弹跳
h=1就是典型的线段树建图,所以可以想到线段树套线段树优化建图。
但是本题的点非常少,所以我们考虑用线段树套平衡树(set)来减少空间。
众所周知,dijkstra算法有一个优秀的性质:每个点只会被标记一次,并且权值非负。
在有多个矩形覆盖一个点的时候,我们肯定是用dis最小的矩形来覆盖。
所以我们可以把每个弹跳装置也看做一个点,跑dijkstra的时候就可以保证每个点都是被最优的矩形所覆盖,所以覆盖过一次的节点可以删去。
时间复杂度O(nlog2n+(n+m)log(n+m)),空间复杂度O(nlogn)。
- P7712 [Ynoi2077] hlcpq
将水平线段的 l,r 都加上 n,加边限制变成:x,y 有连边当且仅当 lx≤y≤rx,ly≤x≤ry。
考虑 tarjan 算法的过程,递归到 u 时,枚举边 (u,v),若 v 没遍历过则需要递归,否则更新 low[u]←dfn[v]
递归的部分可以 O(n) 枚举,更新则是需要求最小值。
所以用主席树维护未访问过的点个数、访问过的点的 dfn 最小值。
修改点为已访问,只需要改对应的叶节点。在找未访问点时,要么找到有效点,要么更新个数为 0,是均摊 O(nlogn) 的。
矩阵乘法
将一些操作转化成矩阵乘法后,可以利用结合律将其优化至O(logn)的计算复杂度
- P1939 【模板】矩阵加速(数列)
⎡⎢⎣fifi−1fi−2⎤⎥⎦=⎡⎢⎣101100010⎤⎥⎦⎡⎢⎣fi−1fi−2fi−3⎤⎥⎦
- P3263 [JLOI2015]有意义的字符串
b−√d2,b+√d2为x2−bx+b2−d4=0的两根。
柿子可以化为xn=bxn−1+d−b24xn−2,而且d−b24还是整数。
设fi=(b−√d2)i+(b+√d2)i,则有fi=bfi−1+d−b24fi−2
[fifi−1]=[bd−b2410][fi−1fi−2]
当n为偶时,0<(b−√d2)n<1
当n为奇时,−1<(b−√d2)n<0
最后输出时判断奇偶即可
- P3193 [HNOI2008]GT考试
f[i][j]+=f[i−1][k](匹配到第k位后加一个字符能匹配到第j位,0≤j,k<m)
构造矩阵A(只有A0,0=1),矩阵B,对于每个满足上述条件的(j,k),让Bi,j=1,其余为0
之后计算A×Bn就行。
差不多的一题:见转换法T3:P3204 [HNOI2010]公交线路
- P6772 [NOI2020] 美食家
f[t+w][u][v]=max{f[t][u][p]+G[p][v]}(pw,G[p][v]−−−−−→v)
由于w≤5,把边可以拆成5条长度为1的边,之后变成
f[t+1][u][v]=max{f[t][u][p]+G[p][v]}(p→v)
定义广义矩阵乘法C=A∗B(可以证明有结合律):
Ci,j=maxk{Ai,k+Bk,j}
令原图邻接矩阵为G,则f[t]=f[0]∗Gt
用A来维护ans[1]
然后只要把美食节按时间排序,之后就对于每个1≤i≤k:
A←A∗Gti−ti−1,A[xi]←A[xi]+yi
就可以维护每个美食节。
预处理G的2z次幂(z为整数),由于点的个数为5n,时间复杂度为O(k(5n)2logT+(5n)3logT)
- P6569 [NOI Online #3 提高组] 魔法值
发现ai很大,n,q很小,就可以发现矩阵乘法的时间复杂度应该是对的。
定义矩阵异或(我自己这么叫)为:
(A⊕B)i,j=xorkAi,k×Bk,j
看看有没有结合律吧:
A⊕B⊕C
=xork(A⊕B)i,k×Ck,j
=xork(xordAi,d×Bd,k)×Ck,j
乍眼一看就知道没有结合律,但是本题中B,C为01矩阵,还可以进行进一步化简:
上述柿子中Ai,d一共异或了∑kBd,k×Ck,j次,所以上述式子可以化为
xordAi,d×((∑kBd,k×Ck,j)&1)
=xordAi,d×(xorkBd,k×Ck,j)
也就是说,当B,C为01矩阵时是有结合律的。
预处理邻接矩阵的2z次幂(z为整数),计算答案时只计算第一列。
此时答案为第一行第一列的数,时间复杂度O((n+q)n2logai)
- P3328 [SDOI2015]音质检测
加一矩阵:⎡⎢⎣FiFi−11⎤⎥⎦=⎡⎢⎣1ab100100⎤⎥⎦⎡⎢⎣Fi−1Fi−21⎤⎥⎦
减一矩阵:a≠0时,⎡⎢⎣Fi−1Fi−21⎤⎥⎦=⎡⎢⎣0101a−1a−ba001⎤⎥⎦⎡⎢⎣FiFi−11⎤⎥⎦
a=0时,⎡⎢⎣Fi−1Fi−21⎤⎥⎦=⎡⎢⎣01001−b001⎤⎥⎦⎡⎢⎣FiFi−11⎤⎥⎦
设V(i)=⎡⎢⎣FiFi−11⎤⎥⎦,那么第i项的值就是V(Ai−1+1)V(Ai−1+1)[0][0]
由于是矩阵[0][0]位置的值,我们算(V(Ai−1+1)V(Ai−1+1)T)[0][0]也不影响答案,而且有:
(G×V(Ai−1+1))((G×V(Ai−1+1))T)=G×V(Ai−1+1)V(Ai−1+1)T×GT,这样我们线段树部分就不会被乘法顺序影响了。
用线段树维护区间矩阵和,支持在前或后乘一个新矩阵即可,时间复杂度O(nlogn)
- P7739 [NOI2021] 密码箱
看到它求值的部分是算多次a+1b,于是考虑把它换成矩阵运算使得它能O(logn)修改。
用[ba]表示ab,那么计算a+1bc就可以变成
[bab+c]=[011a][cb]
答案就可以表示成[011a0][011a1]⋯[011ak]
然后发现W操作就是使[011ak]变成[011ak+1]
可以变成多乘一个[1101]
然后可以发现E操作就是多乘一个[0−112]
用平衡树维护修改达到O(nlogn)即可
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】