NOI2022联合省选 题解

NOI2022联合省选 题解
day1的题解咕咕咕了,有空再写吧(好吧,看到这么长的题面+大模拟就没有写题解的欲望了……

注:非官方题解,不保证绝对正确,做法不一定是官方做法

注2:代码请见这篇blog

d2t1:传送门https://www.luogu.com.cn/problem/P8292
题意:给定一些数字,每次询问一些质数,要求从给定的数字中选出一个子集,使得乘积被所有询问的质数整除,求方案数。
数据范围: n ≤ 1 0 6 , m ≤ 1500 , ∑ c i ≤ 18000 n\leq 10^6, m \leq 1500, \sum c_i \leq 18000 n106,m1500,ci18000,值域范围 2000 2000 2000
sol:注意到这么大的 n n n 其实没用(只要统计每种数有多少个就行了),同样 m m m 的范围其实也没什么用,决定复杂度的其实是 ∑ c i \sum c_i ci 和值域范围。
对于这道题而言,一个数的影响显然只与它的质因子集合有关,因此你可以把所有质因子集合相同的数合并,不过这其实并不本质,因为仍然会剩下 1200 1200 1200 多个数。
2000 2000 2000 的值域范围很耐人寻味,我们可以观察得到这么一个性质:由于 43 × 47 = 2021 > 2000 43\times47=2021>2000 43×47=2021>2000 ,因此一个数最多只会包含一个 ≥ 43 \geq 43 43 的质因子。而 < 43 <43 <43 的质数一共只有 13 13 13 个。
假设我们根本不关心那些 < 43 <43 <43 的质因子(比如,如果询问里没有的话),那这题就很简单了:把所有数按照含有哪个 ≥ 43 \geq 43 43 的质因子进行分类(可能包含某一个,也可能不含),设每一类有 a i a_i ai 个数,那么对于一类而言,如果询问集合里包含这一类的质因子的话,就代表这一类的数至少要选一个,方案数是 2 a i − 1 2^{a_i}-1 2ai1 ;否则就是可以任意选,方案数是 2 a i 2^{a_i} 2ai 。把每类的方案数乘起来即可。
现在要把前 13 13 13 个质因子考虑进来,于是我们可以大力容斥:枚举 {前 13 13 13 个质因子} ∩ \cap 询问集合 的子集,要求所选的数里必须不包含这个子集中的质因子。然后只需要把不符合条件的数扔掉之后重复刚才只考虑 ≥ 43 \geq43 43 的质因子的情形就行了。
总复杂度 O ( 2 13 ∑ c i ) O(2^{13}\sum c_i) O(213ci) ,看起来不小但实测跑得飞快。

d2t2:传送门https://www.luogu.com.cn/problem/P8293
题意:给定一个括号序列,,每个左括号有一个权值。你有如下操作:一是把 (A)(B) 变成 (A()B) ,代价为 x × ( A ) x \times (A) x×(A) 中第一个左括号的权值 $ + y \times (B)$ 中第一个左括号的权值,其中 x , y ∈ 0 , 1 x,y \in {0,1} x,y0,1 ;二是把 AB 变成 BA ,无代价。问把原串变成 (((...()...))) 的最小代价。
sol:仔细观察题目中的操作,发现其实答案只与每个括号在哪一“层”有关。操作一的本质是花一定的代价把一对括号扔进里层,我们总是可以从外到里操作,这样操作到某一层时,这一层里的所有括号就可以用操作二进行任意交换。
用vector记录每层的数,每次操作一可以进一步抽象为“用一个数(即 (A) 中第一个左括号的价值)把另一个数(即 (A)中第一个左括号的价值)放进里层”。下面我们称 (A) 中第一个左括号的价值为“辅助”。
根据 x , y x,y x,y 的不同可能的取值分类讨论:
如果 x = 1 , y = 1 x=1,y=1 x=1,y=1 :假设当前层有 m m m 个数,最大值是 m x mx mx ,最小值是 m n mn mn ,总和是 s u m sum sum ,那么无论是最终被放进去的还是留在外面的至少都要有一次的代价,同时总的代价次数一定是 2 ( m − 1 ) 2(m-1) 2(m1) ,那么显然可以贪心地用最小的数当辅助,把多出来的 m − 2 m-2 m2 次代价全都算在 m n mn mn 头上。同时放尽可能小的数进去使得下一层尽可能小,只需最后用最大的数把最小的数放进去。代价为 ( m − 2 ) × m n + s u m (m-2)\times mn+sum (m2)×mn+sum
如果 x = 0 , y = 1 x=0,y=1 x=0,y=1 :这种情况也很简单,因为代价只与被放进去的数有关,那么就每次贪心地把尽可能小的数往里扔就行了。每一层把最大的数留在外面,这一层的代价为 s u m − m x sum-mx summx
最复杂的情况是 x = 1 , y = 0 x=1,y=0 x=1,y=0 ,即代价算在辅助头上的情况:对于每一层,首先要决策把哪个数留下来,然后用最小的数把其余的数放进去,最后再用留下来的数把最小的数放进去。问题是如何决策呢?
计算所有层的总代价,我们发现除了最里层的数以外,每个数都会在某一层被留下来并计算一次贡献。既然这样,如果我们确定了最里层的数是多少,每一层就可以贪心地留下当前层的(除了要被放进最里层的数以外的)最大数,因为这样可以让尽可能小的数往里走,以减小里层辅助的代价。
很自然的想法是最里层放全局最大数,这样的决策如下:
当还没有遇到全局最大数时,留下本层最大,代价为 m n × ( m − 2 ) + m x mn\times (m-2)+mx mn×(m2)+mx ;遇到了全局最大数时,留下本层次大,代价为 m n × ( m − 2 ) + c d mn\times (m-2)+cd mn×(m2)+cd c d cd cd 为本层次大值)。
这就完了吗?我们发现存在反例:

1 10
9 9

如果第一层把全局最大值 10 10 10 放进去,总代价为 1 + 9 + 9 + 9 = 28 1+9+9+9=28 1+9+9+9=28 ;但如果把 1 1 1 放进去,代价为 10 + 1 + 9 + 1 = 21 10+1+9+1=21 10+1+9+1=21 。问题出在哪里?
在于“遇到全局最大数后把当前层的次大值留下”这一步。如果这一层恰好只有 2 2 2 个数,而被留下的恰好是一个很小的数,就失去了把它放进里层用来当辅助的机会。
不过仔细分析,发现这种情况只可能出现在一开始,即第一层只有两个数的情况(如果第一层只有一个数,显然可以直接忽略这一层,从下一层开始做)。这是因为每层都只会留下一个数,而题目性质保证了每层至少也会多出来一个数,因此如果进入某一层时已经有超过 2 2 2 个数,一直到最后一步之前都一定是超过 2 2 2 个数。
于是我们可以在一开始遇到这种情况时特判把较小的数放进去,但是紧接着又会有问题:如果下一层只有 1 1 1 个数,可能又会面临刚才的尴尬情况。进一步,如果我们面临如下情况:前 k k k 层分别有 2 , 1 , 1 , . . . 2,1,1,... 2,1,1,... 个数, 我们显然不能在每一层都枚举。
进一步,我们把这前 k k k 层一起处理,发现无非只有两种可能:要么把这 k k k 层的最大值扔进里面,用于扔进最里层;要么把这 k k k 层的最小值扔进里面,用于当里层的辅助。无论哪种情况这 k k k 层的代价均为所有数的和减去被放进去的数。
因此只要讨论这两种情况,后面按照原策略贪心即可。
总复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn),瓶颈主要在于排序和/或用set维护当前层的最值。

d2t3:传送门https://www.luogu.com.cn/problem/P8294
题意:给定树,点有点权。每次切断一条边,交换这条边两个端点的点权,代价为这两个点权之和。求把所有边都切断的最小代价。
sol:第一种dp方案:设 f [ x ] [ y ] [ z ] f[x][y][z] f[x][y][z] 表示点 x x x 的子树,把子树内的点 y y y 换出去, 外面的 z z z 换进来的答案。
转移:考虑与 x x x 相邻的 3 3 3 条边的操作顺序:
1、如果先操作 x x x 与父亲的边,则被换出去的是 x x x ,枚举换进来的点 z z z 。假设剩余两条边先操作左子树,那么在左子树里枚举换出去的点 w w w ,把它换到 x x x 的位置之后换进右子树,枚举换出去的点 p p p ,则有: f [ x ] [ x ] [ z ] = m i n ( f [ x ] [ x ] [ z ] , f [ l c ] [ w ] [ z ] + f [ r c ] [ p ] [ w ] + c [ x ] + c [ z ] ) f[x][x][z]=min(f[x][x][z], f[lc][w][z] + f[rc][p][w] + c[x] + c[z]) f[x][x][z]=min(f[x][x][z],f[lc][w][z]+f[rc][p][w]+c[x]+c[z]) 。剩余两条边先操作右子树同理。
2、如果先操作 x x x 与左儿子的边,则左子树换进来 x x x ,枚举换出去的点 w w w ;假设再操作 x x x 与父亲的边,那么换出去 w w w ,枚举换进来的 z z z ;最后把 z z z 换进右子树,枚举换出去的点 p p p ,则有: f [ x ] [ w ] [ z ] = m i n ( f [ x ] [ w ] [ z ] , f [ l c ] [ w ] [ x ] + f [ r c ] [ p ] [ z ] + c [ z ] + c [ w ] ) f[x][w][z]=min(f[x][w][z],f[lc][w][x] + f[rc][p][z] + c[z] + c[w]) f[x][w][z]=min(f[x][w][z],f[lc][w][x]+f[rc][p][z]+c[z]+c[w])
3、如果先操作 x x x 与左儿子的边再操作 x x x 与右儿子的边,那么要把 w w w 换进右子树,枚举换出去的点 p p p ;最后把 p p p 换出去,枚举换进来的点 z z z ,则有: f [ x ] [ p ] [ z ] = m i n ( f [ x ] [ p ] [ z ] , f [ l c ] [ w ] [ x ] + f [ r c ] [ p ] [ w ] + c [ z ] + c [ p ] ) f[x][p][z]=min(f[x][p][z],f[lc][w][x] + f[rc][p][w] + c[z] + c[p]) f[x][p][z]=min(f[x][p][z],f[lc][w][x]+f[rc][p][w]+c[z]+c[p])
4,5:如果先操作 x x x 与右儿子的边,与2,3同理。
虽然看起来枚举很多,但是复杂度是 O ( n 3 ) O(n^3) O(n3) 的,这是因为所有的操作可以归结为:枚举点 x x x ,并从左子树、右子树、子树外各枚举一点。众所周知枚举左右子树的点是 O ( n 2 ) O(n^2) O(n2) 的(类似于树形背包,只在lca处算贡献),再加一个子树外就是 O ( n 3 ) O(n^3) O(n3) 的。
但是这一算法也没法进一步优化了,因为状态本身已经 O ( n 3 ) O(n^3) O(n3) 了。
我们需要换一种状态!现在设 f [ x ] [ v ] [ d ] f[x][v][d] f[x][v][d] 表示点 x x x 的子树内, v v v 被换出去,被换进来的点最终停在距离 x x x d d d 的位置上的答案。并且不计入换进来的点产生的贡献。
我们先看状态数:注意到 v v v d d d 一定不会来自同一侧,因此套用树形背包可知状态数已经是 O ( n 2 ) O(n^2) O(n2) 的了 。
同样分类讨论转移:
1、先操作 x x x 与父亲 :假设再操作左儿子,则换进来的点最终停在左侧。那么枚举右侧深度 d ′ d' d ,左侧换出来的点 v v v 以及右侧换出来的点 v ′ v' v,有: f [ x ] [ x ] [ d ] = m i n ( f [ x ] [ x ] [ d ] , f [ l c ] [ v ] [ d − 1 ] + f [ r c ] [ v ′ ] [ d ′ ] + c [ v ] × ( d ′ + 1 ) + c [ x ] ) f[x][x][d]=min(f[x][x][d],f[lc][v][d-1]+f[rc][v'][d']+c[v]×(d'+1)+c[x]) f[x][x][d]=min(f[x][x][d],f[lc][v][d1]+f[rc][v][d]+c[v]×(d+1)+c[x]) 。再操作右儿子同理。
2、先操作左儿子再操作父亲:则换进来的点最终停在右侧。枚举左侧深度 d ′ d' d ,左侧换出去的点 v v v ,右侧换出去的点 v ′ v' v ,有: f [ x ] [ v ] [ d ] = m i n ( f [ x ] [ v ] [ d ] , f [ l c ] [ v ] [ d ′ ] + c [ x ] × ( d ′ + 1 ) + f [ r c ] [ v ′ ] [ d − 1 ] + c [ v ] ) f[x][v][d]=min(f[x][v][d],f[lc][v][d']+c[x]×(d'+1)+f[rc][v'][d-1]+c[v]) f[x][v][d]=min(f[x][v][d],f[lc][v][d]+c[x]×(d+1)+f[rc][v][d1]+c[v])
3、先操作左儿子再操作右儿子:则换进来的点最终停在 x x x 处。枚举左侧深度 d ′ d' d,左侧换出去的点 v ′ v' v ,右侧深度 d d d ,右侧换出去的点 v v v ,有: f [ x ] [ v ] [ 0 ] = m i n ( f [ x ] [ v ] [ 0 ] , f [ l c ] [ v ′ ] [ d ′ ] + c [ x ] × ( d ′ + 1 ) + f [ r c ] [ v ] [ d ] + c [ v ′ ] × ( d + 1 ) + c [ v ] ) f[x][v][0]=min(f[x][v][0],f[lc][v'][d']+c[x]×(d'+1)+f[rc][v][d]+c[v']×(d+1)+c[v]) f[x][v][0]=min(f[x][v][0],f[lc][v][d]+c[x]×(d+1)+f[rc][v][d]+c[v]×(d+1)+c[v])
4,5:先操作右儿子,与2,3同理。
乍一看枚举的复杂度直接飞起,别急,让我们冷静分析:
1、先枚举 v v v ,然后不难发现无论 d d d 是多少, f [ r c ] [ v ′ ] [ d ′ ] + c [ v ] × ( d ′ + 1 ) f[rc][v'][d'] + c[v] × (d'+1) f[rc][v][d]+c[v]×(d+1) 这一部分的贡献是可以独立计算的。因此先枚举 v ′ v' v d ′ d' d ,计算 t m p = m i n { f [ r c ] [ v ′ ] [ d ′ ] + c [ v ] × ( d ′ + 1 ) } tmp=min\{f[rc][v'][d'] + c[v] \times (d'+1)\} tmp=min{f[rc][v][d]+c[v]×(d+1)} ,再枚举 d d d 更新 f [ x ] [ x ] [ d ] f[x][x][d] f[x][x][d] 即可。
其中,计算 t m p tmp tmp 时先枚举 d ′ d' d ,由于 f [ r c ] [ v ′ ] [ d ′ ] f[rc][v'][d'] f[rc][v][d] 这一项与先前枚举的 v v v 无关,因此可以在枚举 v v v 之前先预处理这一项关于每个 d ′ d' d 的最小值,因此在真正计算 t m p tmp tmp 时就无需再枚举 v ′ v' v 了;
分析这一步的代价:预处理时枚举 v ′ , d ′ v',d' v,d 的代价受到 f [ r c ] [ v ′ ] [ d ′ ] f[rc][v'][d'] f[rc][v][d] 有效的项数限制,是 O ( n 2 ) O(n^2) O(n2) 的;计算 t m p tmp tmp 时枚举的 v , d ′ v,d' v,d 来自不同子树,是 O ( n 2 ) O(n^2) O(n2) 的 ;最后计算 f [ x ] [ x ] [ d ] f[x][x][d] f[x][x][d] 时枚举的 v , d v,d v,d 的代价受到 f [ l c ] [ v ] [ d − 1 ] f[lc][v][d-1] f[lc][v][d1] 的有效项数限制,是 O ( n 2 ) O(n^2) O(n2) 的。
2、 枚举 v , d v,d v,d ,注意到 f [ l c ] [ v ] [ d ′ ] + c [ x ] × ( d ′ + 1 ) f[lc][v][d']+c[x]×(d'+1) f[lc][v][d]+c[x]×(d+1) d d d 无关, f [ r c ] [ v ′ ] [ d − 1 ] f[rc][v'][d-1] f[rc][v][d1] v v v 无关,因此可以事先预处理出前者对于每个 v v v 的最小值和后者对于每个 d d d 的最小值,然后在计算 f [ x ] [ v ] [ d ] f[x][v][d] f[x][v][d] 时就无需枚举 d ′ , v ′ d',v' d,v 了。利用相似的分析方法可以得到这一步的代价也是 O ( n 2 ) O(n^2) O(n2)
3、先预处理 f [ l c ] [ v ′ ] [ d ′ ] + c [ x ] × ( d ′ + 1 ) f[lc][v'][d']+c[x]×(d'+1) f[lc][v][d]+c[x]×(d+1) 对每个 v ′ v' v 的最小值,再枚举 d d d ,然后先枚举 v ′ v' v 求出在当前这个 d d d f [ l c ] [ v ′ ] [ d ′ ] + c [ x ] × ( d ′ + 1 ) + c [ v ′ ] × ( d + 1 ) f[lc][v'][d']+c[x]×(d'+1)+c[v']×(d+1) f[lc][v][d]+c[x]×(d+1)+c[v]×(d+1) 的最小值,最后枚举 v v v 计算 f [ x ] [ v ] [ 0 ] f[x][v][0] f[x][v][0] 即可。这一步也是 O ( n 2 ) O(n^2) O(n2)
最终,总的时空复杂度均为 O ( n 2 ) O(n^2) O(n2)

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值