好题记录

2019.6

Codeforces 1166C A Tale of Two Lands

给定一个数列,求其中满足 \(min\{|x+y|,|x-y|\} \leq min\{|x|,|y|\}\)\(max\{|x|,|y|\} \leq max\{|x+y|,|x-y|\}\)的数对\((x,y)\)个数。
\(n \leq 2 \times 10^5\)

如果去分类讨论\(x,y\)的符号会把问题变得非常复杂。观察条件包含的性质,发现这个条件是与\((x,y)\)符号无关的。也就是说如果将所有数都取其绝对值不会影响答案。

只需要考虑非负数的情况,那么排序之后对于每个\(x\)求出\(y\)的取值范围二分统计一下即可。复杂度\(O(n \log n)\)

Codeforces 437 C. The Child and Toy

给出一个带点权的无向图,每次选择一个点删除,并删除这个点所连的边。删除一个点的代价是此时其所有相邻点的权值之和。问删完所有点的最小总代价。

\(n \leq 1000, m \leq 2000\)

删点的实质是在删边,每条边都会被删一次。正向反向考虑都很难,我们考虑每条边被删除时对答案产生的贡献。每条边被删除时它的两个端点中其中一个产生贡献。为了让答案尽量小,不妨假设每次都是点权较小的那个产生贡献。这就要求对于每条边都要先删除点权较大的点。这样做是否是可行的?如果我们能构造出一个删点顺序满足这样的条件那就是可行的。每条边都规定了一个顺序:点权大的排在点权小的之前。我们可以将每条边看做一条有向边,从点权大的连向点权小的。

这样形成的图一定无环(暂不考虑点权相等的情况),因此一定可以拓扑排序,那也就是说这样的方案一定存在。

而点权相等就可能有环,不过环内点先后是无所谓的。

因此枚举每条边即可求出答案,复杂度\(O(m)\)。为什么数据范围出那么小= =。

Codeforces 1165 E. Two Arrays and Sum of Functions

给出两个长度为\(n\)的序列\(a\)\(b\),定义\(f(l,r)=\sum\limits_{i=l}^ra_ib_i\)。现在可以给序列\(b\)中的元素指定任意顺序,问怎样指定\(b\)的顺序能让\(\sum\limits_{1 \leq l \leq r \leq n}f(l,r)\)最小。

\(n \leq 2 \cdot 10^5\)

我们考虑对于每一个位置\(i\)\(a_i \cdot b_i\)会被统计\(i(n-i+1)\)次。

因此答案可以用这种形式来表示:

\[\sum\limits_{i=1}^na_ib_i \times i(n-i+1) \]

这个式子中除了\(b_i\)之外都是只与\(i\)有关的定值,因此考虑设一个\(c_i=a_i \times i(n-i+1)\),答案转化为求\(\sum\limits_{i=1}^nb_i c_i\)的最小值。

这个问题也就是给你两个序列\(a,b\),问怎么给它们排列顺序能使\(\sum\limits_{i=1}^{n}a_ib_i\)最小。

关于两个序列两两相乘之和最小,有一个贪心的结论:\(a\)从小到大排序,将\(b\)从大到小排序,\(a_i\)配对\(b_i\),这样一定使答案最小

证明:

对于\(a_i,a_j,b_i,b_j\)(排序后的)满足\(1 \leq i < j \leq n\)。仅考虑这两项,现在的结果是\(a_ib_i+a_jb_j\)。如果交换\(b_i,b_j\),那么答案是\(a_ib_j+a_jb_i\)。作差可得

\[a_ib_i+a_jb_j-a_ib_j+a_jb_i=a_i(b_i-b_j) + a_j(b_j - b_i) = (a_i-a_j)(b_i - b_j) \leq 0 \]

因此不交换一定不会劣。

拓展:如果要使\(\sum\limits_{i=1}^na_ib_i\)最大,那么结论是将\(a\)\(b\)都从小到大排序配对。证明类似。

Codeforces 276 D. Little Girl and Maximum XOR

给出\(l,r\),求\(max\{ a \oplus b \} (l \leq a \leq b \leq r)\)

\(1 \leq l,r \leq 10^{18}\)

先将\(l,r\)转二进制。位运算中要使某个数尽量大一定是从高位开始贪心。从最高位开始,如果相同也没有办法凑出\(1\)。直到第一个不同的位,我们能够凑出\(1\)。从这以后因为第一位已经不同的,于是\(b\)中每一位都可以变成\(0\)而不必担心小于\(a\)\(a\)的每一位都可以上升而不必担心大于\(b\)。因此后面的每一位都可以凑出1了。

Codeforces 577 B. Modulo Sum

给出\(n\)个数,问能否选出一个子集使得其和能被\(m\)整除。

\(n \leq 10^6, m \leq 10^3\)

\(n\)个数做前缀和\(s_i\),然后将\(s_i\)\(m\)取模。如果存在\(s_i=s_j\)则区间\([i+1,j]\)内数的和是\(m\)的倍数。根据鸽巢原理,只要\(n>m\)就一定存在这样的区间。

于是限制条件就变成了\(n \leq 10^3\)。直接做背包即可。

Codeforces 552 C. Vanya and Scales

给出两个数\(w\)\(m\),并给出\(101\)个砝码\(w^0,w^1,\cdots,w^{100}\)(每种重量只有一个)。将\(m\)放在左边,现在要在左右放若干砝码,问是否可能平衡。

\(1 \leq w,m \leq 10^9\)

每个砝码可以放左边,放右边,或不放。也就是每个砝码的系数为\(\{-1,0,1\}\),问是否能使其总和为\(m\)。显然\(w=2\)一定满足,从\(w=3\)开始考虑,\(\log_310^9 \approx 20\),因此折半搜索。

Codeforces 578 B. "Or" Game

给出一个序列,可以进行\(k\)次操作,每次可以给序列中的一个数乘一个定值\(x\)。问操作后所有数的最大或和。

\(n \leq 2 \times 10^5, k \leq 10\)

任何一个数乘上一个\(>1\)的数二进制一定左移一位。有了这个结论把\(k\)\(x\)数乘在同一个数上一定最优。

2019.7

Codeforces 1019 A. Elections

\(n\)个人给\(m\)个党投票。可以花\(c_i\)贿赂第\(i\)个人改变投票对象(任意改变),否则第\(i\)个人就会投票给\(p_i\)党。问最少花多少钱能让\(1\)党获胜。
\(1 \leq n,m \leq 3000\)

不确定得票时很难求。考虑枚举\(1\)党得票,贪心计算最少花费。复杂度\(O(n^2)\)

Codeforces 442 B. Andrey and Problem

\(n\)个人,第\(i\)个人有\(a_i\)的概率同意,\(1-a_i\)的概率不同意。请从这些人中选择一些人,使得其中只有一人同意的概率最大。问最大概率。

\(n \leq 100\)

假设我们已经选择了一个集合\(S\),则概率为\(\sum\limits_{i \in S}\left (a_i \dfrac{\prod\limits_{j \in S}(1-a_j)}{1-a_i} \right)\)

\(A=\prod\limits_{i \in S}(1-a_i)\)\(B=\sum\limits_{i \in S}\dfrac{a_i}{1-a_i}\)。则概率为\(AB\)

考虑加入一个\(a_k\)后答案的变化量

\(\Delta=(B+\dfrac{a_k}{1-a_k}) \cdot A \cdot (1-a_k)-SB=A(1-B)a_k\)。因此只要满足\(1-B>0\),那么选择\(a_k\)就能使答案变大。贪心地,我们从大到小选择\(a_k\),直到\(B \geq 1\)为止。

复杂度\(O(n \log n)\)

Luogu P2336 喵星球上的点名

\(n\)个人,每人有两个字符串。给出\(m\)次询问,每次询问给出一个字符串,问这个字符串在多少个人的那两个字符串中的一个里出现过。询问结束后输出每个人被询问到的次数。

\(n \leq 5 \times 10^4, M \leq 10^5\)

\(SA\)后问题转化为区间颜色个数,与每个颜色被覆盖的次数。前者直接用莫队解决,后者考虑差分:每次覆盖到,就对答案加上剩余询问个数次贡献,每次离开,就对答案减去剩余询问个数次贡献。

Codeforces 375 B. Maximum Submatrix 2

给出一个\(01\)矩阵,可以任意交换行的顺序。求交换后可能的最大全\(1\)子矩形面积。

\(1 \leq n,m \leq 5000\)

如果不能交换行,直接单调栈解决即可。

交换行之后每行的排列还是确定的。由于面积没有特殊的性质,我们考虑枚举矩形一条竖边的位置,为了最大化面积,我们将所有行按照每行该位置向右扩展1的最大距离排序,很容易计算面积。复杂度\(O(n^2 \log n)\)

Codeforces 2 B. The least round way

【双价值的处理】给出一个\(n \cdot n\)的矩阵,从左上角走到右下角(向右或向下),经过的数字相乘。怎样走使得得到的结果结尾的0尽量少。

首先处理出每个数的2因子与5因子个数。接着有个重要结论:结尾的0的最小个数,为最小2的路径与最小5路径中的较小值。这样就把双价值的问题转化为单价值的问题了。

反思:双价值总是难以处理的。

Codeforces 463 D. Gargari and Permutations

【建图;问题特性】给出\(k\)个长度为\(n\)排列\(1 \leq n \leq 1000,1 \leq k \leq 5\)),求出LCS。

枚举数对。预处理出每个数对在每个排列中是顺序的还是逆序的。如果对于所有排列某数对都是同一个序的,那么这个数对就是可以作为LCS的一部分的。找出所有这样的数对之后,得到一张\(DAG\),求出最长链即可。

反思:DP的本质是DAG。因此关键是要抓住这张DAG在这里是什么。

Codeforces 449 B. Jzzhu and Cities

【建图;最短路的性质】给出一张带权的无向图。再给出\(k\)条长度\(w_i\)\((1,r_i)\)的路径。问这\(k\)条路径最多能删掉几条,使得删掉之后所有点到1的最短路不变。

将这\(k\)条路径加入图中,跑\(Dijkstra\)。同时每个点统计一个最短路入度。然后依次这\(k\)条路径,如果长度大于相应的最短路那肯定删去。如果恰好等于,那么看最短路入度,大于1则可以删去。然后将最短路的入度减去1.

反思:这其实是一个最短路树(最短路图)的问题。删边问最短路变化的问题一般都这样处理。

Codeforces 471 D. MUH and Cube Walls

【字符串差分匹配】修改字符换匹配的定义:两个字符串的所有字符的差都相同。问匹配数。

我们来分析一下这样匹配有什么特殊的性质,发现他们两两相邻的差是一样的。于是我们可以预处理出两个串的差分数组,然后直接去匹配差分数组就可以了。用KMP算法\(O(n)\)完成就可以了。

反思:匹配的本质就是寻找相同的东西。在这里两个数组相同的东西就是差分。

Codeforces 1156 C. Match Points

给出一个长度为\(n\)的序列\(a\)和一个数\(z\),数对\(1 \leq i < j \leq n\)称为一个匹配当且仅当\(|a_i-a_j| \geq z\)。序列中一个数只能匹配一次。问最多能有多少个匹配。

想到了一个贪心,排序后从前往后匹配差值第一个大于等于\(z\)的。然而被叉了,对于一个右端点在另一个左端点左边的情况,交换一下更优。

事实上可以二分答案,而贪心地验证是可以做到的。一定是去选择两端的依次匹配最优。

反思:直接贪心不可行,也许应用到二分验证中是可行的。

Codeforces 274 B. Zero Tree

给出一棵树\((n \leq 10^5)\),有点权\(v_i\)。每一次操作可以选择一个包含根节点的连通块,使连通块内所有点的权值+1或-1。问最少进行多少次操作才能使整棵树的所有点权都变为0。

根据题意分析,要对一个点进行操作的话必须对它的所有祖先进行相同的操作。而既然这样,想让一个点的点权归零进行操作,那之前就必须已经完成所有其后代的归零操作(因为如果后代没有归零,以后再去操作后代就必定会影响祖先使得决策不优)。而这样就将问题分解为子树上的子问题了。

我们记录\(up_i\)\(down_i\)表示子树\(i\)归零所需要的+1与-1的最少操作次数。子树与子树之间可以通过根节点一并操作。这样根节点的权值在操作子树时至少会变化\(\max\{up_v\}-\max\{down_v\}\),之后再让根节点归零再进行\(|v_u+\max\{up_v\}-\max\{down_v\}|\)次操作。

2019.8

Codeforces 295 E. Yaroslav and Points

给出一个序列\(a_i\),(\(|a_i| \leq 10^9\)),单点修改。询问给出\(l,r\),求\(\sum\limits_{i=l}^{r}\sum\limits_{j=i}^{r}(x_j-x_i)\)

按照权值建线段树(动态开点或离散化),接下来询问的可以看做线段上两两之间的距离之和。分治求解可以得出两边内部两两的距离,于是只需要计算出所有跨越两边的点之间的距离之和就可以了。既然这样,就可以通过维护区间和以及区间点个数求解了。

洛谷5154 数列游戏

给出\(n\)个物品排成一排,第\(i\)个物品有两个值\(a_i\)\(b_i\)。每次可以选择两个\(a\)值不互质的相邻物品移除,并获得他们\(b\)值之和的得分。问最多能得分多少。\((n \leq 800)\)

直接区间DP并不好处理。我们可以先做一次可行性DP,\(f_{i,j}\)表示\([i,j]\)是否可行。\(f\)的转移有两种:一是枚举中断点;二是两端匹配。复杂度\(O(n^3 \cdot log)\)

处理出来后,再DP一次。\(g_i\)表示前\(i\)个的最大得分,分为\(i\)选与\(i\)不选,\(i\)不选直接继承,\(i\)选可以枚举左端点转移。复杂度\(O(n^2)\)

HDU 6601 Keen On Everything But Triangle

给出\(n\)根木棍\(a_i\),每次询问一段区间\([l,r]\),问其中是否存在能拼成三角形的三根,并输出其周长的最大值。\(n \leq 10^5,a_i \leq 10^9\)

考虑对于一些木棍,要使能拼成三角形且周长最长,一定是选择权值相近的三根。因此一个思路是将木棍排序,然后从大到小枚举连续的三根判断能不能拼成三角形。也可以不排序,每次用主席树维护区间第\(k\)大进行判断。

这样的复杂度是\(O(nm \log n)\)。但由于\(a_i \leq 10^9\),要让一个木棍集合不可能形成三角形并使集合大小尽量大,这样一个集合就是斐波那契数列。而斐波那契数列到第\(50\)项就超过\(10^9\),因此我们从大到小最多枚举\(50\)次一定会有解,并且一定是最优的。依然用主席树来维护。复杂度\(O(50m \log n)\)

Codeforces 675 C. Money Transfers

给出一个长度为\(n\)的圆环,第\(i\)个位置有权值\(a_i\),保证权值和一定为0。每个位置的权值可以向左或向右转移,问最少转移多少次使圆环上每个位置的权值都是0。

显然一段长度为\(l\)的和为0的序列最多转移\(l-1\)次,因为相互之间你给我我给你可以只做一次。因此我们的任务是求出怎样划分使得和为0的序列尽量多。

利用前缀和的性质,和为0意味着前缀和的值相等。因此对于每一个前缀和的值用map求出与它相等的值有多少个,巧妙地解决了“环”的问题。

Codeforces 468 B. Two Sets

给出\(n\)个不同数,要将所有数分为两个集合\(A,B\)。再给出两个数字\(a,b\),对于任意一个值为\(x\)的数,如果\(x \in A\),那么\(a-x \in A\);如果\(x \in B\),那么\(b-x \in B\)。输出方案。

否定后件:如果\(a-x \notin A\),那么\(x \notin A\)。等价于如果\(a-x \in B\),那么\(x \in B\)。也就是说只要\(a-x\)存在,那么\(x\)\(a-x\)必须在同一个集合。同理如果\(b-x\)存在,那么\(x\)\(b-x\)也必定在同一个集合。因此用并查集或者\(2-sat\)维护即可。

Codeforces 558 E. A Simple Task

给出一个长度为\(n\)的字符串,给出\(m\)次询问,每次询问给出一个区间\([l,r]\),将这段区间升序或降序排序。最后输出结果。\(n \leq 10^5, m \leq 5 \cdot 10^4\)

考虑用线段树维护区间内每种字母的出现次数。在已知每种字符出现次数的情况下,区间排序等价于\(|\sum|\)次区间覆盖——用线段树维护。

Codeforces 1200 E. Compress Words

给出\(n\)个字符串,每次将第一个和第二个字符串接龙(合并最长前后缀)并合并为一个字符串,输出最终结果。

每一次合并字符串就需要更新,因此暴力\(KMP\)的复杂度退化为了\(O(n^2)\)

考虑字符串哈希,每次在第二个串中从大到小枚举长度,并在第一个串中截取后缀判断哈希值是否相等。

\(hash\{s_{l..r}\}=a_r-a_{l-1} \cdot base^{r-l+1}\)

Codeforces 372 D. Choosing Subtree is Fun

给出一颗树。定义区间\([l,r]\)的权值为包含所有标号$ \in [l,r]\(的点的连通块最小点数。问权值\)\leq k\(的区间最大长度。\)n \leq 10^5$

用双指针代替二分答案。那么我们需要动态维护一个连续区间的连通块最小点数。可以将点数转化为边数,那么要求的等价于从根节点出发走遍所有点的最短路径。

这就是经典的异象石问题。有一个结论:这样的最短路径是所有点按照\(dfs\)序排序后两两之间的距离之和(最后一个连到第一个)。用\(set\)维护,支持加点和删点。

***「NOIP2017」逛公园

给出一张有向图,求从\(1\)\(n\)路径长度不超过\(d+k\)的路径条数。其中\(d\)\(1\)\(n\)的最短路。\(n \leq 10^5,k \leq 50\)

难点在于状态的定义。\(f[u][k]\)表示\(u\)出发到\(n\)走的弯路长度为\(k\)的方案数。

有效路径上的\(0\)环找起来很讨厌,不会。

2019.9

「NOIP2008」双栈排序

给出一个排列,问其是否能双栈排序。定义进栈比出栈优先,进第一个栈比进第二个栈优先。求字典序最小的操作序列。\(n \leq 10^3\)

我的第一种思路是直接贪心模拟,然而贪心是错误的。当同时能进两个栈时,有时进第二个栈有解,进第一个栈却无解。

先来考虑一下单栈排序的情况。对于\(i<j\),如果\(a_i<a_j\),说明\(a_i\)\(a_j\)先出栈。此时如果存在\(j<k\)\(a_k<a_i\),那么\(k\)必须必\(i\)先出栈了,这就意味着\(k\)进栈之前\(i,j\)都在栈内,也就不能保证\(i\)先出栈了,因此矛盾。而对于其他情况,\(i,j\)存在于同一栈内不会矛盾。

这是一个结论:一个序列不能单栈排序,当且仅当存在\(i<j<k\)使得\(a_k<a_i<a_j\)。通过维护最小值,求解的复杂度是\(O(n^2)\)的。

这就给了我们判断两个元素能否在同一栈内的条件。那么能否双栈排序即判定二分图。然后贪心模拟即可。

Codeforces 319 B. Psychos in a Line

给出\(n\)个人,每个人有一个攻击力\(a_i\)。每一轮对于第\(i\)个人,如果\(a_i > a_{i+1}\),那么\(i\)就可以杀掉\(i+1\)。问最少几轮能杀到不能杀。\(n \leq 10^5\)

这个问题整体考虑比较复杂,可以考虑对于每一个个体来分析它被杀的条件。关键在于,只有相邻才可以杀。因此\(i\)要被杀,它之前所以比它小的必须死完。定义\(f_i\)\(i\)第几轮死,那么\(f_i = \max\{f_j\}+1\)。用单调栈来维护是\(O(n)\)

单调栈的优化在于,我可以不用重复统计之前一定比我小的数。在这道题中,我们将一整段比\(i\)小的信息都存到了\(f_i\)中,就避免了重复计算。

Codeforces 1183 E. Subsequences (easy version)

给出一个字符串,求最长的\(k\)个本质不同子序列的长度和。\(n,k \leq 100\)

对于一个字符串,复制一遍之前的本质不同子序列集合,并将新集合内每一个字符串末尾加上这个字符。排序去重,保留前\(k\)个即可。复杂度\(O(n^2k \log k)\)

Codeforces 280 B. Maximum Xor Secondary

给出一个长度为\(n\)的序列(元素互不相同),问所有区间里“最大值异或次大值”的最大值。\(n \leq 10^5\)

对于考虑所有XX的题目好像都不是直接考虑的。一般都是按每个元素来考虑。

考虑\(a_i\)作为次大值:如果最大值在它之前,那么一定是\(a_i\)之前第一个比\(a_i\)大的;如果最大值在它之后,那么一定是\(a_i\)之后第一个比\(a_i\)大的。

这一个过程用单调栈来维护即可。

NOIp2012 开车旅行

预处理\(n\)个数每个数,比自己大的最近的和次近的元素。

以位置为关键字,按照从大到小的顺序将元素插入平衡树(\(set\))中,查询前驱、前驱的前驱、后继、后继的后继四者中离它最近的两个。

2019.10

NOIp2016 愤怒的小鸟

第一象限内有\(n\)个点,问至少选择多少个形如\(y=ax^2+bx 且 a<0\)的抛物线能够覆盖所有点。
\(n \leq 18\)

在这道题中两点确定一条抛物线,使得状压的转移貌似是\(n^2\)的,复杂度\(O(Tn^22^n)\)有点悬。

事实上,我们发现\(n^2\)转移有些浪费。如果一个转移没有包含第一个点,那么之后迟早要回来转移第一个点。既然这样不如每一次转移包含第一个点的,这样就是\(O(n)\)转移的了。可以将这道题积累为一个优化转移的小\(trick\)

SDOI2010 外星千足虫

给定\(m\)个异或方程,问最少使用前几个能够解出唯一解。
n,m \leq 2000

这道题让我联想到了线性基,即先对前\(n\)个方程进行高斯消元,而后对于每一个方程跟线性基一样插入,直到没有自由元位置。但不知道为什么是不对的。

题解的做法是直接对\(m\)个方程做高斯消元。在高斯消元中,第\(i\)个方程要向下寻找第\(i\)项系数为1的方程。在这个寻找的过程中记录最大值就是我们的答案。这可以记为一个结论。

同时,异或方程组由于使用的都是bool数组,可以用biteset优化。

SCOI2009 最长距离

给一个01矩阵,同一全0连通块中两点的距离为其欧几里得距离。现可以移去\(T\)个1,问最远点对距离。
\(n \leq 30\)

正难则反。在同一连通块中等价于存在一条没有1的通路。那么枚举点对,判断要使他们连通最少移去多少个1——这个可以用Floyd预处理出来。

注意点权的Floyd不算起点。

洛谷4215 踩气球

给定长度为\(n\)的序列,并给定\(m\)个区间。每一次操作时给一个点权值-1,每一次操作后询问有多少个给定区间的和为0。
\(n,m \leq 10^5\)

显然复杂度不允许我们每一次操作后遍历所有区间,因此答案的统计得在修改的过程中完成

考虑用线段树维护区间和。我们知道,一个区间可以拆解为线段树上若干节点。如果这若干个节点的值都为0,那么这个区间的和就为0。

在这道题中,线段树上一个节点可以作为若干个给定区间拆解后的节点。modify过程中如果碰到一个节点为0,那么就对对应的若干区间产生贡献。如果一个区间被贡献的次数等于拆解后的节点数,那么这个区间和就为0了。

Codeforces 3 D. Least Cost Bracket Sequence

给一个由'(','?',')'三种字符组成的字符串。将第\(i\)个'?'替换成'('的代价为\(a_i\),替换成')'的代价为\(b_i\)。问替换成合法括号序列的最小代价为多少。
\(n \leq 5 \cdot 10^4\)

如果从前往后替换所有'?',那么任何时候右括号数量都不能大于左括号数量。而最后总共能填的左右括号数量是确定的。因此不妨一开始默认将所有的'?'都替换成')'。到达一定程度之后肯定会出现右括号过量的情况,此时需要考虑将之前的某个')'替换为'('。我们贪心地去替换最优的那个,因此用一个优先队列维护之前所有填了右括号的位置。

Codeforces 1065 F. Up and Down the Tree

给出一棵树,从一个节点可以跳到子树的一个叶子上,叶子节点可以向上跳,但最多跳\(k\)条边。问从根节点出发最多能遍历多少个叶子节点。\(n \leq 10^6\)

一个叶子节点可以直接回到根节点,或者以别的叶子为跳板往上跳,即向上跳\(k\)格然后到子树内另一个深度更浅的一个叶子再往上跳。

我们先看从根节点出发能回来的有多少个叶子,这些叶子都跳掉。然后选择一个子树下去……

从根出发能回来的叶子数量等价于有多少叶子能跳到根。令\(f_u\)表示从节点\(u\)为根出发能回来的叶子数量,这样转移:

\[f_u=\sum\limits_{v \in son_u}f_v[low_v-dep_u \leq k] \]

只要子树最浅的叶子能够到达\(u\),那这颗子树就不会断节,所有能到\(v\)的叶子都能够通过这个叶子到达\(u\);反之就没有叶子能够到达\(u\)

答案就是每次统计完\(f\)之后选择子树往下走,注意如果直接往下走会计算重复。因此在转移\(f\)的时候,一旦转移成功就将\(f_v\)清空,能够避免重复。问题转化为求根节点出发的最长链,再\(dfs\)一遍即可。

CF1238F The Maximum Subtree

给出一颗树,要求选出一个\(size\)最大的连通子图,使得其满足这样的条件:将每个点看做数轴上的一条线段,树上两点之间有边当且仅当对应的线段有交。
\(n \leq 3 \cdot 10^5\)

分析这个条件,不可能存在三条线段相交于同一点,否则就会形成三元环。因此我们得出结论,答案的树的形态就是一条长的链,链上分出长度为1的边。(即一个毛毛虫)。

经过化简,答案本质上就是求点权为“度数-1”的最长链。树形\(DP\)即可。

洛谷P2371 [国家集训队]墨墨的等式

给出\(a_1..a_n\),求\(a\)基于正整数的线性变换在区间\([L,R]\)内能取到的值得个数。即\([L,R]\)内有多少\(B\)满足\(a_1x_1+a_2x_2+ \cdots +a_nx_n=B,x \geq 0\)
\(n \leq 12,a_i \leq 5 \ast 10^5\)

我们的问题是区间内有多少数能被凑出。我们可以分别求出区间\([1,R]\)\([1,L-1]\)的答案。下面以\(R\)为例。

由于加法交换律,我们选定一个\(a_i\)让它最后加。我们记这个\(a_i\)\(m\)。这样的话,一个数\(t\)能被凑出等价于不用\(m\)可以凑出\(t-k \ast m\),这等价于\(t \ \text{mod} \ m\)能被凑出。因此我们只需要考虑\(m\)的剩余系中有多少数能被凑出就行了。可以定义\(f(i)\)为能凑出的模\(m\)等于\(i\)的最小数,记为\(u\)。之所以要最小,因为如果最小答案都\(>R\)那么就显然无解了。这个过程可以用最短路完成,对于\(a_i\),我们可以这样转移\(f((x+a_i)\text{mod} \ m) = \min\{f(x)+a_i\}\)。那么接下来答案就是

\[\sum\limits_{i=0}^{m-1}(\left \lfloor \dfrac{R-f(i)}{m} \right \rfloor+1) \]

洛谷P2939 [USACO09FEB]Revamping Trails

给出一张无向图,可以将至多\(k\)条边变成边权为\(0\)。求\(1\)\(n\)的最短路。
\(n \leq 10^4,k \leq 20\)

由于\(k\)很小,分层图跑\(Dijkstra\)即可。

这道题首先让我联想到了“逛公园”,因此先写了一个\(O(nk)\)\(DP\)(记搜),但是挂成了\(45\)。但是我没有写成松弛的形式,不知道有没有这种实现方法。

Codeforces 280 C. Game on Tree

给出一棵树,起初所有点都是白色的。每次随机选择一个白点,将它以及它的子树全部染黑。问期望染几次以后树上的点全部被染黑。
\(n \leq 10^5\)

期望的线性性质在很多时候能帮助我们拆解问题。比如这道题中,期望的染色次数等于每个点期望的染色次数之和。

期望是所有情况总和的平均值。我们可以考虑随机一个染色序列,按照这个序列来染色。这样的话,一个点被染色一次当且仅当它的祖先没有一个被染过。

2019.11

Codeforces 1225 D. Power Products

给出\(n\)个数\(a_i\)并给出一个定值\(k\),问有多少对\(i,j\)能满足\(a_i \times a_j = x^k\),其中\(x\)为正整数。
\(n \leq 10^5, 2 \leq k \leq 100\)

容易想到两数乘积是\(k\)次幂等价于分解质因数后指数之和为\(k\)的倍数。关键在于怎么统计答案。我们将指数对\(k\)取模,由此对于每个\(a_i\)满足条件的\(a_j\)分解质因数后的形式(指数取模)是可以算出的。所以只要用哈希压缩一个数分解后的表达式,在\(map\)中查询即可。

Codeforces 1242 B. 0-1 MST

给出\(n\)个点的完全图,其中有\(m\)条边的权值为\(1\),其余所有边的权值都是\(0\)。求原图的最小生成树。
\(n \leq 10^5, m \leq 10^5\)

我们只看权值为\(1\)的边。那么可以题型转化为求补图的连通块个数。答案即为连通块个数\(-1\)

我们用并查集来维护连通块,问题在于要避免一个点被重复合并。我们用一个类似链表的\(nxt\)指针,每次暴力合并一段区间,即每个点与它的\(nxt\)合并,最后一个\(nxt\)和当前点合并,最后修改区间内所有点的\(nxt\),这样就能保证每个点不被重复合并了。

Codeforces 1242 C. Sum Balance

\(k\)个箱子,第\(i\)个中有\(n_i\)个数。每个箱子中的数已知,保证所有箱子中的所有数字互不相同。现在要从每个箱子中选出一个数,将这\(k\)个数随意排列后一一放回。问是否存在一种方案使得操作后每个箱子中数的和相同。
\(n_i \leq 5 \times 10^3, k \leq 15\)

“所有数字互不相同”这个条件非常重要。因此对于一个箱子,选出一个数后应该放入哪个数是确定的。我们考虑连一条有向边(不允许同一盒子内的数相连),这样的话如果存在一个环,并且环上所有点所在盒子互不相同,那么这些盒子都能够达到最终状态了。

再通过“互不相同”这个条件,一个数只对应一个数,意味着每个点的出边只有一条。因此不会出现环套环。因此我们很容易找出所有满足条件的环。

并不是每个环都是可选的,我们要选出几个环使得其所在盒子互不相同且并集为全集。由于\(k\)很小,用刷表法状压即可。

posted @ 2019-11-07 15:01  DennyQi  阅读(224)  评论(0编辑  收藏  举报