loj3265 「USACO 2020.2 Platinum」Delegation
因为是最大化最小值,考虑二分答案。
设当前二分的答案为\(K\)。则要判断是否有一种划分方式,使得每条链的长度都至少为\(K\)。
不妨以\(1\)为根,对整棵树dfs。记\(fa(u)\)为\(u\)的父亲节点。dfs(u)
函数求出一个值\(f(u)\),或判断在当前的\(K\)下无解。有解时,我们把\(u\)的子树划分为若干条长度\(\geq K\)的链,并选择一条未完结的链(允许这条链长度\(<K\))覆盖\(u\)和\(fa(u)\)之间这条边。这条链会被交给dfs(fa(u))
继续处理。而\(f(u)\),就是这条返回给\(fa(u)\)的链的最长长度。dfs(u)
函数要做的,就是在保证其他每条链长度都\(\geq K\)的前提下,让\(f(u)\)的长度尽可能大。
考虑dfs(u)
函数的实现。先递归\(u\)的所有儿子,每个儿子\(v\)会带来一条长度为\(f(v)+1\)的链(这个\(+1\)就是\(u,v\)之间的边,它没有被算在\(f(v)\)中)。把得到的这些链按长度排序。此时我们有两种选择:
- 方案一:把所有这些链两两匹配。(如果链的数量是奇数,就加一条长度为\(0\)的链)。要求每对匹配链的长度和\(\geq K\)。并令\(f(u)=0\)。
- 方案二:挑出一条链作为\(f(u)\),让其余的链两两匹配。如果能匹配成功,则令\(f(u)=\)这条挑出来的链的长度。
这里的“两两匹配”,我们可以做一个简单的贪心:让最大的链和最小的链匹配,第\(2\)大的链和第\(2\)小的链匹配......。如果存在某一对链的长度和\(<K\),说明匹配失败,无法找到合法的匹配方案。
当\(u=1\)时,我们显然只能选择方案一,即把所有链都匹配起来。否则无解。
当\(u\neq 1\)时,本着让\(f(u)\)尽可能大的原则,我们优先考虑方案二。如果无法实现方案二,再考虑方案一是否可行。若也不可行,则无解。
现在的问题是,如果选择方案二,我们该如何在保证其它链能够成功匹配的前提下,挑出一条尽可能长的链作为\(f(u)\)呢?考虑两条长度分别为\(x,y\)的链,若\(x<y\),则若挑出\(y\)这条链后其它链能够成功匹配,挑出\(x\)后其他链也一定能成功匹配(这相当于把匹配中的\(x\)换成\(y\),有一条链变得更长了,匹配结果不会变差)。故可以二分把那条链作为\(f(u)\),判断是否可行即可。
时间复杂度\(O(n\log^2n)\)。
loj3266 「USACO 2020.2 Platinum」Equilateral Triangles
图片来源:洛谷用户:ix35
观察一个曼哈顿等边三角形:
红线、蓝线分别是\(BC,AC\)的曼哈顿距离。
把线段平移,得到下图:
此时,\(BC\)的曼哈顿距离是\(\color{red}{\text{红}}\)\(+\)\(\color{green}{\text{绿}}\),\(AC\)是\(\color{blue}{\text{蓝}}\)\(+\)\(\color{green}{\text{绿}}\),于是我们可以得到:\(\color{red}{\text{红}}\)\(+\)\(\color{green}{\text{绿}}\)\(=\)\(\color{blue}{\text{蓝}}\)\(+\)\(\color{green}{\text{绿}}\),所以\(\color{red}{\text{红}}\)\(=\)\(\color{blue}{\text{蓝}}\)。同理可知:\(AO=BO=CO\)(曼哈顿距离)。也就是说,\(O\)是\(\Delta ABC\)在曼哈顿距离意义上的“外心”。
考虑枚举这个外心\(O\),再枚举\(O\)到\(A,B\)的距离\(r\)。大力讨论\(A,B\)所在的象限(四种情况)。此时\(A,B\)的位置就已经确定了。考虑\(C\)。首先\(C\)一定在和\(A,B\)相反的象限,且\(OC=r\)。可以发现这样的\(C\)一定在一条斜\(45^{\circ}\)角的线上(即平行于矩形某条对角线的线)。对每条这样的斜线做前缀和即可\(O(1)\)查询出\(C\)的数量。
时间复杂度\(O(n^3)\)。
loj3267 「USACO 2020.2 Platinum」Help Yourself
把所有线段按左端点排序。设\(dp_0[i][r]\)表示考虑了前\(i\)条线段,最大右端点在\(r\)时,有多少满足条件的线段子集;\(dp_1[i][r]\)表示此时所有满足条件的线段子集,每个线段子集的并的连通块数之和;\(dp_2[i][r]\)表示此时所有满足条件的线段子集,每个线段子集的并的连通块数的平方,之和......。
一般地,定义\(dp_k[i][r]\)表示考虑了前\(i\)条线段,最大右端点在\(r\)时,所有满足条件的线段子集,每个线段子集的并的连通块数的\(k\)次方之和(\(0\leq k\leq K\))。设对于一个线段集合\(s\),\(\operatorname{maxendpos}(s)\)表示\(s\)中所有线段的最大右端点,\(cnt(s)\)表示\(s\)的并的连通块数。则:
\[
dp_k[i][r]=\sum_{s\in[1,i],\ \operatorname{maxendpos}(s)=r}cnt(s)^k
\]
考虑新加入一个线段\(i\)。从\(dp[i-1][j]\)转移到\(dp[i][?]\)。分三种情况讨论:
- \(j<l_i\),此时加入线段\(i\)会使右端点变为\(r_i\),且连通块数\(+1\)。
- \(l_i\leq j\leq r_i\),此时加入线段\(i\)会使右端点变为\(r_i\),且连通块数不变。
- \(j>r_i\),此时加入线段\(i\)既不改变右端点也不改变连通块数。
发现问题主要在于连通块数\(+1\)时的转移不好处理。考虑现在有一\(dp_k[i][j]\),把它的连通块数\(+1\),看它的值会如何变化:
\[
trans(dp_k[i][j])=\sum_{s}(cnt(s)+1)^k=\sum_s\sum_{t=0}^{k}{k\choose t}cnt(s)^t=\sum_{t=0}^{k}{k\choose t}dp_t[i][j]
\]
由此,此时再考虑\(dp_k[i][?]\)的转移式。初始时,令每个\(dp_k[i][j]=dp_k[i-1][j]\),表示在线段集合中不选线段\(i\)的情况。然后考虑选线段\(i\)的情况:
\[
dp_k[i][r_i]+=\sum_{j=0}^{l_i-1}trans(dp_k[i-1][j])+\sum_{j=l_i}^{r_i}dp_k[i-1][j]\dp_k[i][x]+=dp_k[i-1][x]\quad(x>r_i)
\]
其中\(trans(dp_k[i-1][j])\)可以\(O(K)\)求。故时间复杂度为\(O(n^2K^2)\)。
考虑优化。
首先,\(\sum_jtrans(dp_k[i-1][j])\)就等于\(trans(\sum_jdp_k[i-1][j])\)。因为我们在推\(trans\)时并没有用到\(j\)具体的值,只是用\(i,j\)来表示了一堆“线段的集合”。把这些集合先并起来(作为一个更大的集合),再转移也是一样的。
根据套路,不难想到用线段树去维护所有的\(j\)。线段树上,设一个节点所代表的区间为\([l,r]\)。我们在这个节点上存\(K+1\)个值,分别为:\((\sum_{j=l}^rdp_0[i][j]),(\sum_{j=l}^rdp_1[i][j]),\dots,(\sum_{j=l}^rdp_K[i][j])\)。
则从\(i-1\)到\(i\)的转移相当于:
- 对\(r_i\)这个位置执行线段树单点加操作,让它的值\(+trans(\sum_{j=0}^{l_i-1}dp_k[i-1][j])\)。其中求\((\sum_jdp_k[i-1][j])\)要用到线段树区间求和。
- 对\(r_i\)这个位置执行线段树单点加操作,让它的值\(+(\sum_{j=l_i}^{r_i}dp_k[i-1][j])\)。
- 对线段树\(r_i+1\sim 2n\)这些位置执行区间乘操作,全部\(\times2\)。
注意,区间所有的查询操作要在修改操作之前进行。这样查到的才是\(dp_k[i-1]\)的值。
时间复杂度\(O(nK^2+n\log nK)\)。
原文地址:https://www.cnblogs.com/dysyn1314/p/12403868.html