NOI2022 题解

这里是liuzhangfeiabc的NOI2022题解。
目前更新了两天的t1和t2,两天的t3实在没心情去研究了……
注:非官方题解,不保证绝对正确,做法不一定是官方做法。
注2:代码请见这篇文章
 
d1t1:传送门https://www.luogu.com.cn/problem/P8496
给定 n n n 个序列,支持:在某个序列末尾插入元素;删除某个序列末尾的元素;将两个序列首尾相接拼成一个新序列,并销毁原先的两个序列;查询若干个序列(可能重复)首尾相接拼起来形成的新序列的绝对众数(出现次数严格大于一半的数,没有则输出 − 1 -1 1)。
鉴于这是简单题,就直接上正解:注意到我们可以使用链表来维护这个尾部插入、尾部删除、拼接的过程,同时用线段树(或哈希表、map等)来维护每个序列中每个数的出现次数,合并两个序列时可以线段树合并(或启发式合并)。
怎么查询绝对众数呢?注意到一种经典的求绝对众数的方法叫“摩尔投票”,即维护数对 ( a , b ) (a,b) (a,b),表示当前元素和计数器。新来一个元素 x x x 之后,若 b = 0 b=0 b=0 则令 a = x , b = 1 a=x,b=1 a=x,b=1 ,否则若 a = x a=x a=x 则令 b + + b++ b++,否则令 b − − b-- b 。注意到这是一个“两两抵消”的过程,因此如果绝对众数存在,则一定是最后的 a a a ,再扫一遍来检查它是否为绝对众数即可。
另外注意到,这个过程是具有可加性的,即如果在一个集合上做摩尔投票的结果是 ( a , b ) (a,b) (a,b),在另一个集合上的结果是 ( c , d ) (c,d) (c,d),则可以将它们直接“相加”:若 a = c a=c a=c 则结果为 ( a , b + d ) (a,b+d) (a,b+d),否则若 b > d b>d b>d 则为 ( a , b − d ) (a,b-d) (a,bd),否则为 ( c , d − b ) (c,d-b) (c,db),作为这将两个集合合并后的摩尔投票结果。
于是我们可以直接维护每个集合的众数,然后“钦定”一个摩尔投票结果:设一个集合的元素个数为 a a a ,众数为 x x x ,出现次数为 b b b ,则若 x x x 是这个集合的绝对众数(即 2 b > a 2b>a 2b>a),就令摩尔投票结果为 ( x , 2 b − a ) (x,2b-a) (x,2ba),否则直接设为 ( 0 , 0 ) (0,0) (0,0) 即可。
最后,注意由于一个询问中一个集合可能出现多次,出现次数需要用long long存储;另外,如果你你想用deque来维护序列,请千万注意空间限制(由于deque的特性,需要占用大量额外空间,因此如果直接开 1 0 6 10^6 106deque可能会直接MLE)。
总复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn) O ( n log ⁡ 2 n ) O(n \log^2 n) O(nlog2n) ,取决于你的具体实现。
 
 
 
d1t2:传送门https://www.luogu.com.cn/problem/P8497
长度为 n n n 的序列 a a a,每个位置在 l i ∼ r i l_i\sim r_i liri 之间。最开始,你有 k k k 次机会可以使得某个位置 + 1 +1 +1。接下来,你每次可以选择一个位置,使得 a i a_i ai 减小至少 2 2 2,或者选择一个长度至少为 3 3 3 的区间使得其中每个 a i a_i ai 都减小 1 1 1。如果能将所有 a i a_i ai 变为 0 0 0,你就获胜了。问:有多少种初始序列使得你存在获胜方案。
解:先考虑 k = 0 , l i = r i k=0,l_i=r_i k=0,li=ri 怎么做:注意,这实际上相当于“给定序列问是否能取胜”。
考虑dp,令 f [ i ] [ j ] [ j ’ ] f[i][j][j’] f[i][j][j] 表示当前处理到第 i i i 个位置,有 j j j 个操作二至少要延伸到 i + 2 i+2 i+2 位置, j ’ j’ j 个操作二至少要延伸到 i + 1 i+1 i+1 位置,是否可行。注意:这等价于从 i i i 及之前开始并延伸过 i i i 的操作二中,有 j j j 个是从 i i i 开始,有 j ’ j’ j 个是从 i − 1 i-1 i1 及之前开始。
问题是: j j j j ’ j’ j 应该开多大?朴素地开到值域大小显然是不行的,接下来我们将采用一系列操作将其降到常数大小:
首先,由于我们只需要求出一种方案即可,不关心所有方案的总数,可以给方案添加如下的限制规则:
① 优先使用操作一;
② 操作二的长度尽可能短。
如此可得两条较为显然的结论:
1、不会有两个操作二的区间相同。否则,把这两个操作用操作一代替即可;
2、操作二的长度不会超过 5 5 5。否则,拆成两个更短的操作二即可。
结论1示意图
结论2示意图
进一步,我们有如下结论:
3、从任意位置开始的操作二的数量至多为 2 2 2。这是因为,如果有 3 3 3 个操作二从同一位置开始,由结论1和2,它们的长度必然分别为 3 , 4 3,4 3,4 5 5 5。则可以采用如下方式缩短长度:
结论3示意图
由此我们可以推出如下两条结论:
4、上述dp状态中, j j j 的值最大只需设为 2 2 2
5、上述dp状态中, j ’ j’ j 的值最大只需设为 6 6 6
结论5是因为,受到结论2的限制, j ’ j’ j 所涉及到的操作二的起点只可能是 i − 1 , i − 2 i-1, i-2 i1,i2 i − 3 i-3 i3
如此我们已经可以解决 k = 0 , l i = r i k=0,l_i=r_i k=0,li=ri ,只需转移时枚举以 i i i 开始的操作二有多少个即可。
实际上,进一步分析可以得到的结果是:
6、上述dp状态中, j ’ j’ j 的值最大只需设为 2 2 2
证明:对于 j ’ = 3 j’=3 j=3 的情况,只需在结论1和2的限制下列出所有可能的操作二的方案组合,并说明它们均能使用规则①和②进行调整即可。
限于篇幅不再将所有方案一一展开,以下是一个例子:
结论6示意图
接下来考虑 l i = r i , k ≤ 100 l_i=r_i,k\leq 100 li=ri,k100 的情况:我们当然可以在先前dp的基础上多记一位: f [ i ] [ p ] [ j ] [ j ’ ] f[i][p][j][j’] f[i][p][j][j] 表示当前处理到第 i i i 个位置,已经用了 p p p + 1 +1 +1 的机会,有 j j j 个操作二至少要延伸到 i + 2 i+2 i+2 位置, j ’ j’ j 个操作二至少要延伸到 i + 1 i+1 i+1 位置,是否可行。
但是为了后续的分析,我们可以想办法把加的这一维去掉。我们试图求出,为了使得取胜策略存在,在最开始至少要添加多少枚石子。这里我们需要观察到如下结论:除了极特殊的情形外,在一个已经存在取胜策略的局面下添加一枚石子,均存在添加石子的方案使得取胜策略仍存在。
证明:
1、若原先存在一种取胜策略使用了操作一,如将 i i i 位置移除了 x x x 枚石子,则只需将新增的石子放入 i i i 位置,并将该操作改为移除 x + 1 x+1 x+1 石子即可;
2、若原先存在一种取胜策略使用了操作二且区间长度不为 3 3 3,如操作区间为 [ l , r ] [l, r] [l,r],则只需在将新增的石子放入 l l l 位置,并将该操作改为一个在 l l l 位置移除 2 2 2 枚石子的操作一和一个对 [ l + 1 , r ] [l+1, r] [l+1,r] 区间执行的操作二即可;
3、若原先存在一种取胜策略使用了操作二且区间不为 [ 1 , n ] [1, n] [1,n],如操作区间为 [ l , r ] ( r ! = n ) [l, r] (r!=n) [l,r]r!=n,则只需在将新增的石子放入 r + 1 r+1 r+1 位置,并将该操作改为一个对 [ l , r + 1 ] [l, r+1] [l,r+1] 区间执行的操作二即可;
容易看出,同时不符合以上三种情况的只有两种可能:
k = 1 k=1 k=1,且 a i a_i ai 均为 0 0 0
k = 1 , n = 3 k=1, n=3 k=1,n=3 a i a_i ai均为 1 1 1
特判上述两种情况即可。(忘记特判可能挂成25。)
于是我们可以设 f [ i ] [ j ] [ j ’ ] f[i][j][j’] f[i][j][j] 表示当前处理到第 i i i 个位置,有 j j j 个操作二至少要延伸到 i + 2 i+2 i+2 位置, j ’ j’ j 个操作二至少要延伸到 i + 1 i+1 i+1 位置,至少需要额外添加多少枚石子。如果不可达或需要的数量大于 k k k 则记 f [ i ] [ j ] [ j ’ ] = k + 1 f[i][j][j’]=k+1 f[i][j][j]=k+1 。此时上述关于 j j j j ’ j’ j 上界的分析仍然适用。转移时枚举以 i i i 开始的操作二有多少个即可。
现在考虑 a i a_i ai 不再固定的情况,还是先从 k = 0 k=0 k=0 分析:考虑dp套dp,对于一个 i i i ,把所有可能的 f [ i ] [ j ] [ j ’ ] f[i][j][j’] f[i][j][j] 的值的组合压进状态: g [ i ] [ s ] g[i][s] g[i][s] 表示考虑到前 i i i 个位置, f [ i ] f[i] f[i] 状态是 s s s 的方案数。转移时需要枚举下一个 a i a_i ai 的值,直接枚举是不可接受的,但只需注意到 a i a_i ai 最多枚举到 8 8 8 就可以覆盖所有可能的转移了。此时状态总数只有 2 9 = 512 2^9=512 29=512 种,可以预处理所有转移。
对于 k < = 100 k<=100 k<=100 的情况呢?我们只需对新的 f f f 进行状压, s s s 中的每一位从 0 / 1 0/1 0/1 变成 0 ∼ 101 0\sim 101 0101 的数。乍一看状态总数高达 10 2 9 102^9 1029,但我们只需用dfs/bfs预处理所有有用的状态,即可发现有用的状态数只有 8765 8765 8765 种。
复杂度 O ( n m ) O(nm) O(nm) ,其中 m m m 为状态数。实际上,其实 f f f j = j ’ = 2 j=j’=2 j=j=2 的状态也是不需要的,可以使得状态数减少到 7660 7660 7660 种, a i a_i ai 只枚举到 7 7 7 即可。当然或许还有其他状压方法,对状态总数的分析也不一定需要这么繁琐,实测最终的状态数不超过 30000 30000 30000 均能通过此题。
 
 
 
d2t1:传送门https://www.luogu.com.cn/problem/P8499
给定两棵有根树 T 1 T1 T1 T 2 T2 T2 ,问能否将 T 1 T1 T1 删去若干个非根节点的点后与 T 2 T2 T2 同构。保证两棵树的点数之差 k ≤ 5 k \leq 5 k5 。两棵有根树同构定义为一者可以经过重标号后得到另一者,且根在重标号后相同。
解:对于 k = 0 k=0 k=0 的情形,显然我们可以直接做树哈希。鉴于网上的相当多的树哈希写法都是有问题的,这里给出一个正确性较好的树哈希写法:动态地给每种子树分配一个编号(具体实现时可以开一个从哈希值到编号的map),然后对于每个点,将其所有子树的编号(而非哈希值)取出,排序后进行序列哈希。为减少碰撞,底数取得比 2 n 2n 2n 大且使用双模数即可。
然后考虑 k > 0 k>0 k>0 的结果:我们记chk(x,y,k)表示 T 1 T1 T1 中以 x x x 为根的子树能否在删除不超过 k k k 个点的前提下与 T 2 T2 T2 中以 y y y 为根的子树同构。注意这个 k k k 是必要的,不一定等于二者的子树大小之差,这有助于我们以后剪枝。对于原问题,只需求chk(T1.root,T2.root,|T1|-|T2|)即可。
我们考虑怎样求chk(x,y,k):首先是一些平凡的情形:如果 y y y 为空,则只需判断 x x x 的子树大小是否不超过 k k k;如果 x x x 的子树与 y y y 的子树同构,显然结果为true。如果 x x x 子树大小比 y y y 小,或 x x x 子树大小比 y y y 大超过 k k k,或 x x x 的儿子数量比 y y y 少,显然为false
然后递归地比较 x x x y y y 的所有孩子:注意到这一过程实际上是 x x x y y y 的孩子两两匹配的过程。假设有两个孩子是同构的,显然可以贪心地匹配;否则,每一个 x x x 的未匹配上的孩子都需要删除至少一个点才能跟 y y y 的某个孩子(也可能为空)匹配上,因此没匹配上的 x x x 孩子数量不能超过 k k k,下设这一数量为 s s s;接下来,在 y y y 的没匹配上的孩子中补一些空孩子使其数量等于 s s s,然后暴力枚举 x x x y y y 的每个没匹配上的孩子,分别设为 u u u v v v,递归到chk(u,v,k-s+1)(注意这里的 k − s + 1 k-s+1 ks+1 便是考虑到了每个没匹配上的孩子都至少要删一个点这个性质)。最后做一个二分图匹配即可(由于 k k k 很小,也可以用暴力dfs或霍尔定理代替)。
这看起来是一个最坏 O ( n 2 ) O(n^2) O(n2) 的大暴力,实际上我们可以看到, chk函数中这个限制 k k k 起到了很好的剪枝作用,可以证明:每个 T 1 T1 T1 中的节点至多参与 2 k 2^k 2kchk过程(证明过程略)。再算上每个点处的二分图匹配过程,可得总的复杂度上界为 O ( n 2 k k 3 ) O(n2^{k}k^{3}) O(n2kk3),当然由于这个上界实际上远远跑不满,因此程序跑得飞快。
 
 
 
d2t2:传送门https://www.luogu.com.cn/problem/P8500
长度为 n n n 的非负整数序列,给定 m m m 个限制,形如区间 [ l i , r i ] [l_i,r_i] [li,ri] 内的最小值恰好为 v i v_i vi 。求一个合法的序列使得严格逆序对数(即数对 ( i , j ) (i,j) (i,j) 满足 i < j i < j i<j a i > a j a_i > a_j ai>aj 的数量)尽可能少。无解输出 − 1 -1 1
解:首先注意到权值范围太大是没有必要的,因为求得是严格逆序对,因此将整个序列全部填为限制中给出的数总是不劣的,因此可以将值域离散化;同时给出多于 n n n 个取值不同的限制一定是无解的,因此值域可以限制在 1 ∼ n 1\sim n 1n
先来看“判定是否存在解”的问题,而这其实是一个经典问题:将所有限制升序排序,维护每个位置可能的最小值是多少。每加入一个限制,就相当于对区间 [ l i , r i ] [l_i,r_i] [li,ri] 进行一次整体赋值为 v i v_i vi 的操作。最后扫一遍所有的限制,维护区间最小值,查询 [ l i , r i ] [l_i,r_i] [li,ri] 的最小值是否恰好是 v i v_i vi,如果不是说明这个限制的最小值是取不到的,因此就无解了。
然后,记如此求出的每个位置可能的最小值为 b i b_i bi ,接下来该如何操作呢?你可能会设计一系列的贪心策略然后取最优。实际上,这个题的良心之处在于,你随便想一个看起来还算合理的贪心几乎就都是对的!接下来我们不会去严格地证明某一种贪心的正确性,而是从部分分出发,讲解如何想到一个比较合理的贪心策略:
先看部分分B,相当于“给定序列中的一些位置之后求答案”。稍加思索不难发现剩余位置一定是单调不降的,而且正是由于这一特性,你可以对每个位置独立考虑,即仅考虑已经填好的位置对这个位置的贡献,并贪心地令这个贡献最小。如何维护呢?注意到对于位置 i i i 而言,如果 j < i j<i j<i a j a_j aj 已经填好,那么 i i i 位置填 [ 1 , a j − 1 ] [1,a_j-1] [1,aj1] 会产生一个贡献,反之若 j > i j>i j>i 则是 [ a j + 1 , n ] [a_j+1,n] [aj+1,n] 会有贡献。我们可以从左往右扫描每个未填的位置,用线段树区间加、区间求最小值来维护每个位置填的贡献最小的数是多少。当外层扫过一个已经填好的数之后,就意味着其贡献区间从 [ 1 , a j − 1 ] [1,a_j-1] [1,aj1] 变成了 [ a j + 1 , n ] [a_j+1,n] [aj+1,n],在线段树上区间修改即可。
再来看部分分A,即值域只有 1 1 1 2 2 2 的情形。我们可以先将那些最小值为 2 2 2 的限制区间全都填上 2 2 2(然后就不用管这些限制了),然后把剩余位置填上 1 1 1。接着我们从后到前扫过每一个 1 1 1,尝试将其变成 2 2 2。如果变成 2 2 2 会使答案变得更优(可以直接记录前后的 1 1 1 2 2 2 各有多少个来判断),并且不会使得某个最小值为 1 1 1 的限制区间爆掉,我们就贪心地将其变为 2 2 2。如何判断会不会使限制区间爆掉呢?我们可以对于每个限制区间,记录一个 q i q_i qi 表示这个区间最靠左的能填 1 1 1 的位置(这可以在前一步判断是否有解时顺便用线段树时处理出来)。扫描时,维护一个标记 p o s pos pos 表示从这里到当前位置至少需要有一个 1 1 1 以满足限制,扫到区间的右端点时加入区间,更新 p o s pos pos 的值(与 q i q_i qi max ⁡ \max max),然后如果 p o s = i pos=i pos=i 则说明这里必须填 1 1 1 以满足限制,否则就可以随意填了。最后,如果这里还是填了 1 1 1,则当前的所有限制都已经被满足,置 p o s = 0 pos=0 pos=0 即可。
最后将两个部分分拼成正解。对于已经求得的 b i b_i bi 序列,我们从后往前扫描,尝试在不违反限制的前提下将每个位置增大到最佳位置。注意到每一种权值的限制都是彼此独立的,因此可以用与部分分A相同的方式处理每个位置是否需要保持不动以满足限制。然后再用部分分B的思想,贪心地将当前位置修改为能使得当前序列的逆序对数最少的值即可,仍然可以用线段树来维护。具体实现时,你可以先直接在 b b b 序列的基础上构造出答案序列,再用树状数组求逆序对数。
虽然没有给出严格证明,但结合两档部分分,这一做法还是比较容易想到且易于理解的。复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn)
 
后记:6000多字的题解终于写完了,感谢你看到最后。
以及不要问我关于“出题人是谁”之类的问题,问就是保密

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值