CodeForces 选讲 by zyb

这篇文章其实原来是讲一点简单的 GCD 卷积,但是发现适用范围不广,所以改为 cf 选讲,应该会记录最近做的杂题。

原题目为 1 ∼ 2 1\sim2 12

原序

确实有 gcd 卷积这么一回事,但是感觉这里用得不是很贴切,毕竟这里目前还在讨论一些简单的问题。

题目

1. CF1884D Counting Rhyme

题意: ∑ 1 ≤ i < j ≤ n ∏ 1 ≤ k ≤ n [ a k ∤ gcd ⁡ ( a i , a j ) ] \sum_{1\le i< j\le n}\prod_{1\le k\le n}{[a_k\nmid\gcd(a_i,a_j)]} 1i<jn1kn[akgcd(ai,aj)]

首先转换一下题意,将 ∀ 1 ≤ k ≤ n \forall 1\le k\le n ∀1kn a k a_k ak 的所有倍数打上标记,即求 gcd ⁡ ( a i , a j ) \gcd(a_i,a_j) gcd(ai,aj) 未被打上标记的组数。根本来说,是要求一个给定 x x x 满足 gcd ⁡ ( a i , a j ) = x \gcd(a_i,a_j)=x gcd(ai,aj)=x 的组数。这是这类问题的关键。

枚举一组数的 gcd ⁡ \gcd gcd 进行分别统计,是这类计数题的关键。但是我们发现直接求 gcd ⁡ \gcd gcd 为定值的组数不好算,但是求 gcd ⁡ \gcd gcd 为定值的倍数容易算,即设在 a i a_i ai x x x 的倍数有 y y y 个,那么一定有 y ( y − 1 ) y(y-1) y(y1) 组。然后考虑容斥。设 f x f_x fx 表示 gcd ⁡ \gcd gcd 为定值 x x x 的组数, g x g_x gx 表示 gcd ⁡ \gcd gcd 为定值 x x x 的倍数的组数,则有以下转移: f x = g x − ∑ 2 ≤ k ≤ n x f k ⋅ x f_x=g_x-\sum_{2\le k\le \frac{n}{x}}{f_{k\cdot x}} fx=gx2kxnfkx

然后这题就可做了。复杂度 O ( n log ⁡ n ) \mathcal{O}(n\log n) O(nlogn)

code

2. CF1900D Small GCD

题意: ∑ 1 ≤ i < j < k ≤ n f ( a i , a j , a k ) \sum_{1\le i<j<k\le n}{f(a_i,a_j,a_k)} 1i<j<knf(ai,aj,ak) f ( a , b , c ) f(a,b,c) f(a,b,c) 表示 a , b , c a,b,c a,b,c 中较小两项的 gcd ⁡ \gcd gcd。(以下假设有 a ≤ b ≤ c a\le b\le c abc

和上一题一样的思路,枚举 gcd ⁡ \gcd gcd x x x,统计 f ( a , b , c ) = x f(a,b,c)=x f(a,b,c)=x 的三元组数。这和上一题不同的是 c c c 不一定要是 x x x 的倍数,它可以是 a i a_i ai 中大于等于 b b b 的任何数。考虑到排序不影响计算,所以从小到大排序,那么假设 b b b 的下标为 i i i,那么 c c c n − i n-i ni 种取法。所以需要枚举 x x x 的倍数,以它为 b b b,那么已经枚举的数都可能是 a a a c c c n − i n-i ni 种取法,故再乘上 n − i n-i ni。实现时只要用 vector 记下一个数的所有在 a i a_i ai 中的倍数即可,空间上为 O ( 128 n + m ) \mathcal{O}(128n+m) O(128n+m)。然后照着上一题容斥即可。

复杂度 O ( 128 n + n m + m log ⁡ m ) \mathcal{O}(128n+n\sqrt m+m\log m) O(128n+nm +mlogm)。枚举因数的 m \sqrt m m 可以进一步优化,但我还不会。

注: ≤ 1 0 5 \le 10^5 105 的整数的因数个数不会超过 128 128 128 个。

code

3. CF1905E One-X

原来以为是数据结构,但是发现是思维题。

发现线段树每一层的块长最多只能有相差为 1 的两个数,所以抓住这作为切入点。考虑对于一个点,给出长度,求有多少点集的 lca 为该点。经过简单容斥计算,如果长度为 1,答案为 1;若否,则为 2 l e n − 2 ⌊ l e n 2 ⌋ − 2 ⌈ l e n 2 ⌉ − 1 2^{len}-2^{\lfloor\frac{len}{2}\rfloor}-2^{\lceil\frac{len}{2}\rceil}-1 2len22len22len1。然后我们乘上这个点的编号就是这个点的贡献,乘上长度相同的点的编号和,就是长度相同的点的总贡献。根据之前的发现,按长度分类,点最多有 2 log ⁡ 2 n 2\log_{2} n 2log2n 种,所以我们继续思考如何计算长度相同的点的编号和。是一种递推,每一层记长度较小值和较大值的编号和和点个数,长度为偶数只能向下传到一种,长度为奇数则要分开传到下一层两个。然后就可以一层层往下推了,推的过程中统计答案即可。

复杂度 O ( t log ⁡ 2 n ) \mathcal{O}(t\log^2n) O(tlog2n)

评价:似乎和官方题解有一点出入,难度似乎不太大,但是不是一下就能想到的。

code

4. CF1905F Field Should Not Be Empty

发现交换的位置只可能有两种:

  1. a i ≠ i a_i\neq i ai=i 时, a i , i a_i,i ai,i
  2. a i = i a_i=i ai=i 时, 1 ∼ i − 1 1\sim i-1 1i1 最大值和 i + 1 ∼ n i+1\sim n i+1n 最小值。

至于为何只有这些操作,因为其它操作本质上不可能使答案增加。(或者说使 i i i 成为好的节点,这样做是一个必要条件)

虽然只有 n n n 种操作,但对操作的影响直接统计不便,所以考虑枚举 a i a_i ai,算各种操作对它的是否有影响,如有,则将该操作的贡献加一(就是表示操作可以增加的好的节点)。

当然要分两种操作讨论:对第一种,我们直接模拟操作,然后计算它会不会变成好节点,这可以线段树维护。对第二种,如果 1 ∼ i − 1 1\sim i-1 1i1 中只有一个大于 a i a_i ai,那么就可以计算一次贡献。

复杂度 O ( n log ⁡ n ) \mathcal{O}(n\log n) O(nlogn)。具体实现看代码。(自认为是比较好理解)

评价:第一步都不好想到,想到了也写不来,难难题。

code

5. CF1909C Heavy Intervals

第一眼考虑排序不等式,想到用小的 c i c_i ci 去匹配更大的的 r i − l i r_i-l_i rili。又有 ∑ r i − ∑ l i \sum r_i-\sum l_i rili 为定值,所以肯定让更大的 r i r_i ri 匹配更小的 l i l_i li,用更小的 r i r_i ri 匹配更大的 l i l_i li。然后我就想不下去了,因为这个“更大”或“更小”似乎没有严格的标准。

不妨让我们来思考这样一个例子: l 1 = 1 , l 2 = 2 , r 1 = 3 , r 2 = 4 , c 1 = 1 , c 2 = 2 l_1=1,l_2=2,r_1=3,r_2=4,c_1=1,c_2=2 l1=1,l2=2,r1=3,r2=4,c1=1,c2=2。稍微算一下,不难发现,我们这样匹配: 2 × ( 3 − 2 ) + 1 × ( 4 − 1 ) = 5 2\times(3-2)+1\times(4-1)=5 2×(32)+1×(41)=5。进而,我们可以证明并推广:当 l 1 < l 2 < ⋯ < l n < r 1 < r 2 < ⋯ < r n , c 1 < c 2 < ⋯ < c n l_1<l_2<\dots<l_n<r_1<r_2<\dots<r_n,c_1<c_2<\dots<c_n l1<l2<<ln<r1<r2<<rn,c1<c2<<cn 时,这样匹配最优: c n ( r 1 − l n ) + c n − 1 ( r 2 − l n − 1 ) + ⋯ + c 1 ( r n − l 1 ) c_n(r_1-l_n)+c_{n-1}(r_2-l_{n-1})+\dots+c_1(r_n-l_1) cn(r1ln)+cn1(r2ln1)++c1(rnl1)。同时,我们可以发现,一个 r i r_i ri,如果在 l , r l,r l,r 中第一个比它小的是 l j l_j lj,那么 r i − l j r_i-l_j rilj 一定是不劣的。简单来说,我们的贪心策略就是对于一个 r i r_i ri 要匹配的是比它小的第一个未使用的 l i l_i li。理由是只有这样,按照上面我们证明的内容,对于任意合法 l , r l,r l,r 的子串才会成立(考虑可能一个 < < < 中间可以夹一些其它元素)。

然后可以栈维护 + 排序。复杂度 O ( n log ⁡ n ) \mathcal{O}(n\log n) O(nlogn)

code

6. CF1909D Split Plus K

我们每次操作只针对一个元素,但是会得到多个元素。因此,作用于一个元素以及这个元素会得到的其它元素的总操作,最终使他们变成相同的一个元素。考虑一个数最终可以变成哪些相同元素。设操作 T T T 次,则有最终相同元素 b = a i + T k T + 1 = a i − k T + 1 + k b=\frac{a_i+Tk}{T+1}=\frac{a_i-k}{T+1}+k b=T+1ai+Tk=T+1aik+k。那么不定的只有 T + 1 T+1 T+1,所以用 gcd ⁡ ( a i − k ) \gcd(a_i-k) gcd(aik) 即可知最终元素,倒退操作次数。值得注意 a i − k a_i-k aik 要么全正,要么全负,到么全零,不然怎么变都不会相同。

评价:感觉比上一题简单。

code

7. CF1909E Multiple Lamps

首先找规律,假如我们将所有按钮都按下,那么会使 ⌊ n ⌋ \lfloor\sqrt n\rfloor n 盏灯亮起,发现增长速率小于 ⌊ n 5 ⌋ \lfloor\frac{n}{5}\rfloor 5n,所以计算得 n ≥ 20 n\ge20 n20 时,只要按下所有开关即可。同时 n ≤ 4 n\le4 n4 时显然无解,也排除。剩下 5 ≤ n ≤ 19 5\le n\le19 5n19。(其实这里也有一些数可以通过按下所有开关通过)。

分析一下,发现我们现在对每一个测试点希望能在 1 0 4 10^4 104 数量级的复杂度内解决。实际上对于 19 19 19 的数据范围也是比较宽裕了,直接枚举所有最终的亮灯情况,复杂度 ∑ i = 1 3 ( 19 i ) = 1159 \sum_{i=1}^{3}{19\choose i}=1159 i=13(i19)=1159。感觉很有前途,进一步,我们从灯亮的情况唯一推到按按钮的情况(就是按灯从小到大的编号枚举,如果枚举位灯状态不符就按按钮),然后判断是否满足 ( u i , v i ) (u_i,v_i) (ui,vi) 的关系。

至于为何不枚举按按钮的情况,因为直接处理 ( u i , v i ) (u_i,v_i) (ui,vi) 的若干限制不方便。我尝试过缩点,但是之后拓扑搞不出来。(但总感觉可以,或许是错觉)

对于单个测试点,最后复杂度为 O ( ( n + m ) ∑ i = 1 ⌊ n 5 ⌋ ( n i ) ) \mathcal{O}((n+m)\sum_{i=1}^{\lfloor\frac{n}{5}\rfloor}{n\choose i}) O((n+m)i=15n(in))

评价:思维性很高,感觉比前面的题的难度上升了一大截。

code

  • 44
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值