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)\)个数。

\(x,y\)的符号都需要分类讨论,好像有点复杂。事后明白了符号在这道题里是不影响的。

我们可以这样来考虑。对于两个正数\(x,y(x<y)\),如果仅改变符号不会影响\(|x+y|\)\(|x-y|\)的值,那么我们就不用考虑符号。那么我们只需要证明\((x,y)\)\((-x,y)\)达成的值一样就可以了。这样的话,一旦\((x,y)\)成立,则\((-x,y)\)\((x,-y)\)也成立,则\((-x,-y)\)成立。即一旦\((x,y)\)成立,则\((|x|,|y|)\)成立。这个证明起来也容易。当数对为\((x,y)\)时,\(|x+y|=x+y\)\(|x-y|=y-x\)。当数对为\((-x,y)\)时,\(|-x+y|=y-x\)\(|-x-y|=x+y\)。故证明成立。

很多时候这种数学的证明不要刻意去分类讨论。因为\((x,y)\)\((y,x)\)是等价的,因此只要证明其中一种就可以了。

AtCoder Beginner Contest 126 D. Even Relation

【树上奇偶性问题】要求给一棵树上的节点染色(0/1),要求如果有两个节点颜色相同,则其间距离为偶数。

树上两个节点\(u,v\)距离的定义是\[dep_u+dep_v-2*dep_{lca(u,v)}\]因此要使距离为偶数,当且仅当\(dep_u+dep_v\)为偶数。而这当且仅当\(dep_u\)\(dep_v\)奇偶性相同。

因此我们只需要计算每个点到根节点的距离是奇数还是偶数。所有奇数的染成一种颜色,所有偶数的染成另一种颜色即可。

事实上,我们还有一种更简单的实现方法,一个点到根节点的距离奇偶性取决于父亲的奇偶性以及它与父亲之间的距离。因此如果一个点和它父亲距离为偶数,则与父亲同色,否则与父亲异色。

Codeforces 437 C. The Child and Toy

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

删点的实质是在删边,因为到最后所有点都会被删完。每条边的贡献一定是其两端的一个点。理论上的最小值就是每条边都选择较小的那个点来贡献。这要求我们对于每条边都要先删除点权较大的点。这让人想起了……拓扑排序!我们将每条无向边都改为从点权大的点连向点权小的边的有向边,就能形成一张\(DAG\)进行拓扑排序。然而点权可能相等就可能有环,环内的点先后是无所谓的,可以tarjan缩点。所幸这道题不需要输出方案。我觉得可以出一道题要求输出方案的。

Codeforces 1175 C. Electrification

【二分答案】给出一个数轴上的序列。求一个位置\(x\),使得\(x\)到所有点的距离中第\(k\)小的距离尽量小。

考虑二分第\(k\)小距离。这样问题就在于\(O(n)\)判定可行性了。而其可行性等价于是否存在一个位置\(x\),使得\([x-mid,x+mid]\)内能够包含至少\(k\)个点。而一次判定中\(mid\)是确定的,问题转化为一个长度为\(2*mid\)的区间是否能覆盖至少\(k\)个点。用一个队列就能完成这个问题了。

Codeforces 1175 D. Array Splitting

【最大值的表达式】给出一个序列,要将其割\(k-1\)刀变成\(k\)个块。第\(i\)块的价值为\(i*\sum\limits_{j∈第i块} a_j\)。求最大总价值。

不是斜率优化DP!\[ans=max\{k*(s_n-s_{k_1})+(k-1)*(s_{k_1}-s_{k_2})+ \dots + (s_{k_{k-1}}-s_{0}) \}\]发现能约掉变成\[ans=k*s_n-\sum\limits_{i=1}^{k-1}s_{k_i}\]于是对前缀和排序求解即可。

Luogu 1875 佳佳的魔法药水

【Dijkstra思想】给出\(n\)个物品的价格\(a_i\),再给出\(m\)个关系,关系为1个\(x\)物品与1个\(y\)物品能够合成1个\(z\)物品。问得到1物品的最小价格。

显然\(a_i\)最小的那个的价值一定确定为\(a_i\),因为其余价值都比\(a_i\)大,通过合成肯定不会优。由此,我们每次选出一个未确定价值的物品中最小的那一个,这样保证了它不可能被更新。

在统计方案数的时候我们发现会重复统计。于是我们每次只令两个已确定价值的物品去更新别人。

有点玄学。

Codeforces 1165 E. Two Arrays and Sum of Functions

【序列两两乘积之和最小的贪心策略,覆盖次数的计数问题】给出两个长度为\(n\)的序列\(a\)\(b\),定义\(f(l,r)=\sum\limits_{l \leq i \leq r}a_i \cdot b_i\)。求\(\min\{\sum\limits_{1 \leq l \leq r \leq n}f(l,r)\}\)。(\(n \leq 2 \cdot 10^5\)

要求的答案等价于\(\min\{\sum\limits_{1 \leq l \leq r \leq n}\sum\limits_{l \leq i \leq r}a_i \cdot b_i\}\)。于是我们考虑对于每一个位置\(i\)\(a_i \cdot b_i\)会在答案中出现多少次。直接统计有些困难,我们不妨按照覆盖点\(i\)的区间右端点依次考虑。右端点为\(i\)时,左端点可以取\([1,i]\);右端点为\(j\)时,左端点可以取\([1,i]\)……综上,左端点可以取\([1,i]\),右端点可以取\([i,n]\)。因此能覆盖点\(i\)的区间共有\(i \cdot (n-i+1)\)个(结论)

因此答案可以用这种形式来表示:\(\min\{\sum\limits_{1 \leq i \leq n}a_i \cdot b_i \cdot i \cdot (n-i+1)\}\)

根据题目条件,除了\(b_i\)之外都为定值,不妨设\(c_i=a_i \cdot i \cdot (n-i+1)\)。则答案转化为\(\min\{\sum\limits_{1 \leq i \leq n}c_i \cdot b_i\}\)

关于两个序列两两相乘之和最小,有一个贪心的结论:对两序列从小到大排序,\(b_i\)应当配对\(c_{n-i+1}\)。(结论)

设有任意\(a_i,a_j,b_p,b_q\)(排序后的)满足\(1 \leq i < j \leq n\)\(1 \leq p < q \leq n\)。有两种方案\(A = a_i \cdot b_q + a_j \cdot b_p\)\(B = a_i \cdot b_p + a_j \cdot b_q\),我们用作差法比较大小。\(A-B=a_i \cdot (b_q-b_p) + a_j \cdot (b_p - b_q) = (a_i-a_j) \cdot (b_q - b_p)\),显然\(a_i-a_j \leq 0, b_q-b_p \geq 0\),因此\(A-B \leq 0\),因此\(A \leq B\)。于是无论对于哪种情况,选择\(A\)肯定不会不优,于是贪心结论成立。

Codeforces 1154 E. Two Teams

【均摊复杂度,双向链表】给出一个长度为\(n\)的序列,第\(p\)次操作要求找到最大值,然后向左右扩散\(k\)个,标记为\(p \ mod \ 2\)。扩散完后的元素(包括最大值那个)全部删除。问最后每个元素的标记情况。

利用双向链表来实现。其中找最大值的过程可以用堆来维护,如果已被删除就继续pop。这样复杂度一定是\(O(n \log n)\)。因为没有插入操作,还可以不用堆,直接排序效果是一样的。

这道题还是挺有启发性的,这道题不需要线段树来求最小值,因为求的是全局的最小值。不需要奇怪的方法维护长度,可以通过双向链表实现,因为肯定不会有元素被标记多次。分析时要针对问题分析。

Codeforces 1148 D. Dirty Deeds Done Dirt Cheap

【思维,隐含条件】给出\(n\)对数对\((a_i,b_i)\)。要求按一定顺序选出若干对,满足\(a_{i1}<b_{i1}>a_{i2}<b_{i2}> \dots\)\(a_{i1}>b_{i1}<a_{i2}>b_{i2}< \dots\) 问最多能选几对

显然可以分为两组。我们来讨论\(a_i<b_i\)的这种。每组只要保证\(b_i>a_{i+1}\)就行了。而如果满足\(b_i>b_{i+1}\),那么一定满足\(b_i>a_{i+1}\)。于是我们只需要将数对按照\(b_i\)从大到小排序即可,所有数对都是选中的。

没有仔细分析需要满足的条件是什么。如果仔细分析就能够发现方法。还是要仔细去看一道题目所具有的性质吧。

Codeforces 276 D. Little Girl and Maximum XOR

【位运算】给出\(l,r\),求\(max\{ a \oplus b \} (l \leq a \leq b \leq r)\)

先将\(l,r\)转二进制。显然需要从高位开始贪心。前面相同的先忽略,对于第一个不同的位,我们发现显然异或能达到1. 考虑后面的位,因为第一位已经不同的,于是r中每一位都可以下降,l中每一位都可以上升。于是每一位都可以凑出1. 于是答案就出来了。

Codeforces 577 B. Modulo Sum

【模运算的性质】给出\(n\)个数,问能否选出一个子集使得其和能被\(m\)整除。\(n \leq 10^6, m \leq 10^3\)

考虑一个特殊的性质:对\(n\)个数做取模后的前缀和,根据鸽巢原理得出只要\(n>m\),则必定有前缀和的值相同。前缀和相同的意义就是这之间的数之和被\(m\)整除——这就是答案。因此只要\(n>m\)就一定有解。

于是限制条件就变成了\(n \leq 10^3\)。背包即可。背包的转移有点困难,因为要避开0。此时不妨使用刷表法。由于需要前后转移,不能做一维的滚动数组。

## Codeforces 552 C. Vanya and Scales

【题型转化,折半搜索】给出两个数\(w\)\(m\)\(1 \leq w,m \leq 10^9\)),并给出101个砝码\(w^0,w^1,\cdots,w^100\)(每种重量只有一个)。将\(m\)放在左边,现在要在左右放若干砝码,问是否可能平衡。

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

Codeforces 578 B. "Or" Game

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

位运算最大值又来了,从高到低贪心。想让一个高位变成1,尽量要把\(x\)乘在同一个数上。于是问题转化为给序列中的一个数乘上\(x^k\),求最大或和。或的逆运算好像不存在,看似不太好维护。正确的做法是维护或的前缀和以及后缀和。这样就以每个数为界限将序列劈成了两半。

猜你喜欢

转载自www.cnblogs.com/qixingzhi/p/11237089.html