2024.11.15 NOIP 模拟赛 题解

T1 数字(number)gym 105257 G. Disappearing Number

题意

\(0\sim n\) 中十进制表示下不含 \(a_{1\sim m}\;(0\le a_i\le 9)\) 的数字数量,\(n\le10^{18}\),多测 \(t\le10^5\)

分析

\(n\) 的十进制表示为 \(v_{1\sim l}\),其中 \(a_1\) 为最低位

\(C=\{x\mid x\notin \{a\},0\le x\le 9\}\)

先特判 \(0\) 是否合法

剩余合法数字可以分为两类类:

  1. 数位小于 \(l\) 的,这部分有 \(\sum_{i=1}^{l-1}\sum_{j\ne0,j\in C}|C|^{i-1}\),其中前一个 \(\sum\) 枚举数位,后一个枚举最高位
  2. 数位等于 \(l\) 的,令 \(j\) 为最小的数满足 \(v_j\notin C\)(若不存在则为 \(0\)),则答案为 \(\sum_{i=j}^{l+1}\left(\sum_{j=0}^{v_{i-1}}[j\in C]|C|^{i-2}\right)\),其中前一个 \(\sum\) 枚举和 \(n\) 的最大公共前缀为 \(v_{l\sim i}\),后一个枚举第一位不相同的(若 \(i=1\) 则令括号中式子为 \(1\)

时间复杂度 \(O(tw\log_{10}V)\),其中 \(w=10\) 为字符集大小

实现精细可以做到 \(O(t\log_{10}V)\)

代码

T2 加训(train)CF733E Sleep in Class

题意

给定一个字符序列 \(a_{1\sim n}\;(a_i\in\{'L','R','O'\})\),初始位于 \(p\),若 \(a_p='O'\) 则直接结束;若 \(a_p='L'\) 则令 \(a_p\leftarrow 'R',p\leftarrow p-1\);若 \(a_p='R'\) 则令 \(a_p\leftarrow 'L',p\leftarrow p+1\)。对于每个 \(1\le p\le n\),求出结束前移动的距离(\(p\pm1\) 为移动一单位距离)。\(n\le5\times10^5\)

分析

\(a\)\('O'\) 分割开

显然 \('O'\) 的位置答案是 \(0\)

以下的 \(a_{1\sim n}\) 为分割后的一段(\(a_i\ne 'O'\),这也是原题的问题,只不过将 \('L'\)\('R'\) 换为 \('D'\)\('U'\)

对于一个位置 \(p\),设其左侧的 \('R'\) 分别位于 \(L_{1\sim x}\)\(L_i>L_{i+1}\)),其右侧的 \('L'\) 分别位于 \(R_{1\sim y}\)\(R_i<R_{i+1}\)

RLLLLRLRLLLLRRRLRLRRLRR
^    ^ ^   ^   ^ ^  ^
L3  L2 L1  p  R1 R2 R3

\(a_p='L'\),则先回从 \(p\) 移到 \(L_1\),并将 \(a_{(L_1,p]}\) 范围都设为 \('R'\),将 \(a_{L_1}\) 设为 \('L'\)。然后从 \(L_1\) 向右移到 \(R_1\),将 \((L_1,R_1)\) 设为 \('L'\),将 \(a_{R_1}\) 设为 \('R'\)。······。若 \(x\le y\),则最后会从 \(R_{x}\) 向左一直移出范围;否则从 \(L_{y+1}\) 向右移出范围

前者的答案为 \((p-L_1)+(R1-L_1)+(R_1-L_2)+\cdots+(R_x-L_x)+(R_x-0)=p-2\sum_{i=1}^xL_i+2\sum_{i=1}^xR_i\),后者答案为 \((p-L_1)+(R_1-L_1)+(R_1-L_2)+\cdots+(n+1-L_{y+1})=p-2\sum_{i=1}^{x+1}L_i+2\sum_{i=1}^xR_i+n+1\)

显然可以在扫描的同时 \(O(1)\) 维护

同理可以得到 \(a_p='R'\) 时的结果

总时间复杂度 \(O(n)\)

代码

T3 排序(sort)P8996 [CEOI2022] Abracadabra \(\quad\) LOJ #3813. 「CEOI2022」Abracadabra

题意

给定 \(a^0_{1\sim n}\)\(a^i=f(a^{i-1})\)\(q\) 次询问 \(t,i\),查询 \(a^t_i\)\(a^0\) 为一个排列,\(n\le2\times10^5,q\le10^6,t\le10^9,2|n\),其中 \(f\) 变换如下:

f(a):
    newa=array[n]
    cnt=0
    j=n/2+1
    for i from 1 to n/2:
        while j<=n and a[j]<a[i]:
            newa[++cnt]=a[j++]
        newa[++cnt]=a[i]
    while j<=n:
        newa[++cnt]=a[j++]
    return newa

即归并排序省略了向下递归

分析

\(nx_i\)\(i\) 之后第一个大于 \(a_i\) 的位置(不存在为 \(n+1\)

将序列按 \([1,nx_i),[nx_i,nx_{nx_i}),\cdots\) 分段

显然段的第一个值组成的序列递增

\(f\) 变换相当于将横跨 \([1,n/2]\)\([n/2+1,n]\) 两个区间的段从 \(n/2\) 之后的位置断开,前面的为一段,后面部分再割成若干段,然后将所有段按第一个元素排序,并拼成一个长度为 \(n\) 的序列。若不存在横跨的段,则序列不变

显然 \(f^{n-1}(a)=f^n(a)\)(因为序列至多被割开 \(n-1\) 次),因此令询问的 \(t\)\(n\)\(\min\)

对于没有被割开的段,显然变换之后位置不变

因此建立树状数组,位置 \(i\) 维护起始\(i\) 的段的长度

因为段起始值构成序列有序,因此可以树状数组上二分求出 \(n/2+1\) 位置所属的段的起始值(即为树状数组中 \(n/2+1\) 前第一个有值的下标)和该位置到所在段起始点的距离(就是 \(n/2+1\) 位置到所求下标的距离)

若没有割开这段,则之后序列都不会变化

否则先跟新前面部分的长度,然后根据 \(nx\) 求出 \(n/2+1\) 开始的段的长度并插入树状数组

由于询问的 \(t\) 无序,因此需要将所有的询问离线下来,放到时间轴上,当处理到那个时间断面时回答相关询问;否则需要可持久化树状数组,不知道能不能做,就算可以,时间空间应该都比较卡

时间复杂度 \(O((n+q)\log n)\)

luogu 代码

loj 代码

T4 改造(modification)QOJ 5013 A. Astral Birth

题意

给定一个长为 \(n\)\(0/1\) 序列,对于每个 \(1\le k\le n\),求出将序列分为 \(k\) 段重排序后的最长不下降子序列的最大值,\(n\le3\times10^5\)

分析

思路 1

基本和 9.7 的 T4 相同

对于一个给定的 \(0/1\) 序列 \(a\),其最长不下降子序列长度为

\[\begin{aligned} &\max_{0\le k\le n}\left(\sum_{j=1}^k[a_j=0]+\sum_{j=k+1}^n[a_j=1]\right)\\ =&\max_{0\le k\le n}\left(\sum_{j=1}^k[a_j=0]-\sum_{j=1}^k[a_j=1]+\sum_{j=k+1}^n[a_j=1]+\sum_{j=1}^k[a_j=1]\right)\\ =&\max_{0\le k\le n}\left(\sum_{j=1}^k\left([a_j=0]-[a_j=1]\right)+\sum_{j=1}^n[a_j=1]\right)\\ =&\sum_{j=1}^n[a_j=1]+\max_{0\le k\le n}\sum_{j=1}^k\left([a_j=0]-[a_j=1]\right) \end{aligned}\]

若令 \(s_i=[a_i=0]-[a_i=1]=\begin{cases}1&a_i=0\\-1&a_i=1\end{cases}\),则 \(a\) 的最长不下降子序列长度为 \(\sum_{j=1}^n[a_j=1]\)\(s\) 的最大前缀和

显然无论如何重排序,\(\sum_{j=1}^n[a_j=1]\) 都不变

因此问题转化为对于每个 \(k\),求出序列 \(s\) 分为 \(k\) 段重排序后的最大前缀和的最大值

显然最优决策是把 \(s\) 割开后,将总和 \(>0\) 的段放到前面,\(<0\) 的放到后面,\(=0\) 的任意,最大前缀和即为所有总和 \(>0\) 的段的和

\(s\) 中分离出一段(与其它段不相连),若是前缀或后缀则需要一次分割,其余需要两次

\(rs(i)\) 为分割了 \(i\) 次的答案(等于题目中分为 \(i+1\) 段的答案,分为一段,即原序列,需要特判)

枚举 \(L,R\),分别表示是否分离出前缀/后缀

则对于每个 \(k'\)\(rs(2k'-2-L-R)\) 可以对 \(F(L,R,k')\)\(\max\)\(F(L,R,k')\) 表示是否强制选择前后缀的情况下,从 \(s\) 中选出 \(k'\) 段的最大总和

求出一个序列选出 \(k\) 段的最大总和有一个经典技巧,类似反悔贪心,每次选择 \(s\) 的最大子段,加到答案上后将这个区间中每个数变为其相反数

若强制选择前缀,则令 \(0\) 位置为极大值(此题的极大值在 \(1e6\) 级别以上就够了),这样第一次选择时一定会选前缀,注意答案要减去这个极大值。后缀同理

总时间复杂度 \(O(n\log n)\),常数较大

代码

思路 2

对于 \(0/1\) 序列中相同的连续段,可以作为一个整体考虑(显然不会从连续段中间割开)(实际上这点在 思路 \(1\) 的代码中体现了)

这样合并后的段一定 \(0/1\) 相间

则划分为 \(k\) 段的答案就是最长的形如 \(000\cdots000111\cdots111\) 的子序列的长度,满足该子序列来自不超过 \(k\) 个段

这相当于删去若干个段使之合法,最大化剩下的段的总长

显然不会删去连续的两段,否则保留其中一段一定严格更优

同时若删去了其中一段,则其左右相邻段(如果有)会合并为一个新段

设原序列总段数为 \(m\),则可以进一步转化为对于每个 \(k\),求出从所有段中选出若干段删去,满足删去段的总权值为 \(\max(0,m-k)\)(边上的段的权值为 \(1\),中间的为 \(2\)),且选出段两两不相邻,最小化删去段的总长度,对应的答案即为序列总长减去该值

分类讨论边上的两段是否取后,可以转化为反悔贪心,堆维护即可

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

思路 3

猜测答案序列是上凸的

可以分治加闵可夫斯基和优化 \(dp\)

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

比赛结果

\(100+85+30+0\),预估的是 \(100+100+45+45\),丢了 \(75\)

posted @ 2024-11-15 19:07  Hstry  阅读(36)  评论(0)    收藏  举报