今日学习: DP && 树上数据结构
DP:
基本DP我便不再累述,安利一下我的博客
这里我就是想讲一下老师上课讲的一些稀奇古怪 毒瘤DP题。(这里先讲一下我已经理解了的)
例1:树上互质最长链;给出⼀棵n个节点的树,每个节点上有点权a_i。求最⻓的树上路径,满⾜条件:路径上经过节点(包括两个端点)点权的gcd和不等于1
每一个点权<=2*105,考虑将每一个点都分解质因数。
对于每一个节点u,设dp[u,p]表示从u点向下挂出的点权都能被p整除的最长链长度。
枚举父亲和儿子的质因数,有相同的便可以进行转移,中途累计答案即可。
例2:给出一个n个节点的树,对于1~n中的每个数k,求出最多能选出多少条互不相交的路径(边,点都不交)且路径长度为k?
考虑如果k是固定该怎么求?
考虑贪心:每一次我们枚举最高点在u的链的放置方案,这样链的两端一定插在不同的儿子中。我们发现每一个儿子对父亲的贡献只有向下到达的最深点,因为任意两条链不能重叠。那么每一次找寻儿子中最深的两个,如果大于k那么就放置,否则更新当前u的最深处。
现在得出单个k的做法,如果令f[k]表示k时的答案,发现由于n的限制,答案最多是 n k n\over{k} kn,考虑这个整除,f[k]的取值最多有 s q r t ( n ) sqrt(n) sqrt(n)取法,并且发现f[k]是单调不升的,那么只要确定一个权值相同的区间的左端点f[]的值,便可以二分出右端点的位置。然后再用以上贪心方法求出下一个区间左端点的f[]的值,以此类推…
时间复杂度为O ( n l o g n s q r t ( n ) ) (nlognsqrt(n)) (nlognsqrt(n))
例3:Easy Problems;长为n的字符串,要求删除一些字符是的子序列中不包含"hard",已知每个位置删除的代价ai,求出最小代价。
滚动数组:用f[1~4],f[1]表示目前为止没有h的最小代价,f[2]表示目前为止没有ha的最小代价,f[3]表示目前为止没有har的最小代价,f[4]表示目前为止没有hard的最小代价;
for(int i=1;i<=n;i++)//当前的位置
{
if(a[i]=='h') f[1]+=b[i];//到目前为止使没有h的最小价值
if(a[i]=='a') f[2]=min(f[1],f[2]+b[i]);//到目前为止使没有ha的最小价值,为了使ha不连接,要么使h全消失,要么使a全消失
if(a[i]=='r') f[3]=min(f[2],f[3]+b[i]);//同理到目前为止使没有har的最小价值,前面已经得出使ha不连接的最小值,再和使r全消失的代价进行比较
if(a[i]=='d') f[4]=min(f[3],f[4]+b[i]);//到目前为止使没有hard的最小价值,前面得出使har无法连接的最小值,与使d全消失的代价比较即可,这样得出使没有hard一定是代价最小的
},最后输出f[4]就是到n位置使没有hard的最小代价了
例4:给出⼀个字符串S,这个字符串只由’A’, ’C’, ‘G’, ‘T’四个字⺟组成。
对于每个1~|S|中的每⼀个i,求出满⾜以下条件的字符串T的个数:
1.⻓度为m。
2.只由’A’, ‘C’, ‘G’, ‘T’四个字⺟组成。
3.LCS(S,T) = i.
答案对10^9+7 取模后输出。
数据范围
|S| <= 15, m <= 1000.
经典的DP套DP(毒瘤 )
考虑传统的求LCS,设f[i][j]表示考虑A串的前i位,考虑B串的前j位的LCS长
有如下:
1.
a
[
i
]
!
=
b
[
j
]
a[i]!=b[j]
a[i]!=b[j]
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
,
f
[
i
]
[
j
−
1
]
)
f[i][j]=max(f[i-1][j],f[i][j-1])
f[i][j]=max(f[i−1][j],f[i][j−1])
2.
a
[
i
]
=
b
[
j
]
a[i]=b[j]
a[i]=b[j]
f
[
i
]
[
j
]
=
m
a
x
f
[
i
−
1
]
[
j
]
,
f
[
i
]
[
j
−
1
]
,
f
[
i
−
1
]
[
j
−
1
]
+
1
f[i][j]=max{f[i-1][j],f[i][j-1],f[i-1][j-1]+1}
f[i][j]=maxf[i−1][j],f[i][j−1],f[i−1][j−1]+1
明显有
f
[
i
]
[
j
−
1
]
<
=
f
[
i
]
[
j
]
<
=
f
[
i
]
[
j
−
1
]
+
1
f[i][j-1]<=f[i][j]<=f[i][j-1]+1
f[i][j−1]<=f[i][j]<=f[i][j−1]+1
因此可以将差分序列压起来,在本题中,令序列a=T,序列b=S,那么对于⼀个确定的T的前缀,当前的LCS状态可以⽤⼀个15位的⼆进制数来表示。
转移时枚举T新添的字⺟是什么,并使⽤上⾯传统的LCS计算⽅法来求出新的状态即可,累计方案.
感觉DP真的是博大精深,还有DP套DP这样的操作,震惊。
DP的精髓在于状态的设置以及转移方程,再加上一些优化。
树上数据结构
常见的树上数据结构:
1.树链剖分 2.线段树 3.主席树
常用算法:倍增思想,点分治,树上路径交
对于以上,我目前只会敲一些经典的板子。
一.支持修改边权和询问路径边权最大值:可用树剖+线段树
二.路径上第 k 大点/边权:可用主席树的前缀和性质 (目前还不会利用树剖去做)
三.距离不超过 k 的路径条数以及距离等于k的路径是否存在:点分治
树上路径交:
两条树上的路径[a,b]和[c,d]有交,则有lca(a,b)在[c,d]上或lca(c,d)在[a,b]上。
其实只要深度大的lca在另一条链上就好了,所以设x=lca(a,b)深度较大。
充分性证明:x在[c,d]上,则[a,b]和[c,d]显然有交。
必要性证明:x不在[c,d]上,如果[a,b]上有点y与[c,d]有交,因为lca(c,d)深度较小,所以y的深度必定小于x,那么x就不是lca(a,b)了,矛盾,所以如果x不在[c,d]上,[a,b]与[c,d]无交。
判断x在[c,d]上只需要判断x是lca(c,d)的儿子且x是c或d的父亲。
树上问题的拓展千奇百怪,还会有许多的算法,数据结构联系在一起,需要问题的转化,目前还不太会(坑)