HNOI 选做
[HNOI2016] 大数
考虑设 \(s[i]\) 表示 \(i\) 的后缀所代表的数膜 \(p\) 意义下的取值,那么一段区间 \([l, r]\) 膜 \(p\) 意义下的取值就是 \(\dfrac{s[l]-s[r+1]}{10^{r-l+1}}\),如果 \(p\ne 2, 5\),那么我们只需要求出有多少对 \([l, r]\) 满足 \(s[l] = s[r + 1]\) 即可,这个可以直接采用莫队很好求解。
对于 \(p=2, 5\) 的情况,发现只需要判断末尾是否为 \(2, 5\) 的倍数即可。
[HNOI2016] 序列
考虑莫队,考虑如何从 \([l, r]\) 变成 \([l, r + 1]\) 或者 \([l,r - 1]\)。总得来说可以归为以 \(l/r\) 为左右端点的所有另一端点在 \([l, r]\) 区间内的所有区间的最小值之和。首先我们可以利用莫队,离线一下所有的询问,然后用线段树可以简单的做到 \(O(N\sqrt{N}logN)\)。考虑有没有 \(O(N\sqrt{N})\) 的做法。
对于每个点,记录其左右第一个比它小的元素,观察可以发现,一段区间的贡献之和是可以直接用前缀和来统计的,假设左边第一个比他小的元素为 \(l_i\),前缀和数组为 \(pre_i\),那么我们有 \(pre_i=pre_{l_i}+(i-l_i)\times a_i\),后缀和同理,那么以 \(r\) 为右端点的区间的答案就可以直接表示成 \(sum[r] - sum[l - 1]\),然后答案会对当且仅当最小值到 \(r\) 这段区间,直接 \(RMQ\) 找到这段区间的最小值即可。
[HNOI2016] 最小公倍数
首先只有一维很好处理。考虑二维的情况,将每条边按照 \(a_i\) 排好序,然后对其分块,块内按照 \(b_i\) 排序,类似于回滚莫队的思路,每次新加 \(\sqrt{N}\) 条边,然后用并查集判断一下即可,复杂度 \(O(N\sqrt{N}α(N))\)。
[HNOI2017] 大佬
首先\(1,3,4,5\)操作无论那一天做都是没有影响的,而\(2\)操作是有影响的,于是可以单独考虑
可以根据\(dp\)求出至少要多少次\(2\)才能让自己不死,于是现在问题变成了给定一个\(c_i\),询问是否可以通过那些操作在\(N\)次以内凑出
考虑到天数只有\(100\),并且加嘲讽能力只能做两次,于是我们考虑使用\(bfs\),求出处理出每一个数最少用多少天,然后把所有的数排序,利用双指针,找到合法的地方即可
[HNOI2017] 抛硬币
首先答案是:
然后发现这玩意儿并不好算,于是搞一种别的方法:
考虑到如果\(A=B\),那么每一种胜利方案都为一对应一种失败方案(反转),总方案是已知的,所以不难求出答案
然后对于不相等的,分两种情况讨论:第一种情况:反转之后胜负不变;第二种情况:胜负改变。
设第一种情况方案数是\(S_1\),第二种情况方案数是\(S_2\),那么\(S_1+S_2\)是一个定值,由于每一个\(S_2\)都可以通过反转得到胜利的,于是其实答案是\(S_1+\dfrac{S_2}{2}\),所以我们只需要求出\(S_1\ or\ S_2\)即可
考虑求出\(S_1\),我们只需要保证:\(a>b, A-a<B-b\)即\(0<a-b<A-B\),由于\(A-B\)只有\(10^4\),考虑计算。
枚举每一个\(a-b\),每一种都是合法的,总方案数为:\(\sum_{i=1}^{A-B-1}\sum_{b=0}^B\dbinom{B}{b}\times \dbinom{A}{i+b}=\sum_{i=1}^{A-B-1}\sum_{b=0}^B\dbinom{B}{B-b}\times \dbinom{A}{i+b}\)
发现这是一个卷积的形式,套用范德蒙德卷积,有答案为:\(\sum_{i=1}^{A-B-1}\dbinom{A+B}{B+i}\),套用拓展卢卡斯即可
[HNOI2018] 寻宝游戏
首先考虑 \(\&\) 操作:\(1\ \&\ 1=1, 1\ \&\ 0=0, 0\ \&\ 1=0, 0\ \&\ 0=0\)。可以发现:如果后一位是 \(0\),那么结果恒为 \(0\),否则的话结果不变。
再考虑 \(|\) 操作:同样可以发现:如果后一位是 \(1\),那么结果恒为 \(1\),否则结果不变。
我们考虑所有的 \(0/1\) 串只有一位的情况,假设我们需要这一位为 \(0\),考虑最后一次从 \(1\to 0\) 的时候,一定是存在一个 \(0\),然后上面的操作符号是 \(\&\),再此之后的所有符号必须都为不变,也就是当前若为 \(1\),则上面必须填入 \(\&\),否则必须填入 \(|\)。而最后一个 \(1\to 0\) 的位置往上的符号我们可以任意填入。需要这一位是 \(1\) 同理。
经过观察可以发现,如果我们把 \(\&\) 视作 \(1\),把 \(|\) 视作 \(0\),那么操作序列也会是一个二进制数 \(op\),同理原串也是一个二进制数\(x\)(两者都是从下往上看的)。那么如果我们需要最后一位是 \(0\),那么我们需要满足 \(x\le op\),否则需要满足 \(x>op\)。于是这道题就转化成了一个比大小的题目,我们求出所有需要为 \(1\) 的位置的最小值,求出所有需要为 \(0\) 的位置的最大值,那么操作序列 \(op\) 一定满足 \(x\le op < y\),所以最后的答案就是 \(y - x\)。
[HNOI2018] 转盘
首先停顿一定是不优的,因为我们可以选择起点,所以停顿可以覆盖的部分一定更少,因此题意可以转化成,有一个大小为 \(N\) 的环,每个点有一个出现时间,我们选择一个点作为起点,不停地走,直到标记所有物品。
考虑固定起点,显然我们可以只关心最后一圈我们的出发时间。那么假设起点为 \(x\),最后一圈从 \(x\) 出发的时间为 \(t\),断环为链,那么我们需要保证对于任意点 \(i\in [x, x + n]\):\(i-x+t\ge T_i\),设 \(a_i=T_i-i\),那么只要满足 \(a_i\le t-x\) 即可。这种方案对应的最后的答案为 \(t+n-1\)。
考虑固定 \(x\) 之后,那么 \(t\) 实际上就是 \(max(x+a_i)\),因此我们要求的实际上是:
记录 \(\{a_i\}\) 的后缀 \(\max\),不难发现这是一个单调不增的函数,考虑查询一段区间的最小的 \(x\),如果右区间最大值大于左区间,那么左区间不可能作为答案,递归右区间;否则右区间就是本身的答案,递归左区间即可。
[HNOI2018] 游戏
首先每个点一定是可以到达一段区间的,不妨设为 \([l_i, r_i]\),如果我们预处理出所有的 \([l_i, r_i]\),那么询问就可以很简单的处理。
对于一扇门,由于其钥匙唯一,假设它的钥匙在门的左边,那么右边的点一定不能到达左边的点,在门右边同理。我们进行标记,此时每个位置就会分成几种情况:只能从左往右,只能从右往左,哪边都去不了,两边都可以去。把从左往右看成一条向上的直线,把从右往左看成一条向下的直线,那么原序列会变成类似于一些山峰的形状。
考虑一个“坡”的答案,我们知道地处是一定不能前往高处的,那么我们从低往高看,不难发现一个“坡”的某个端点是递增或者递减的。暴力维护一下即可。然后把所有“峰”单独暴力一下即可。
[HNOI2019] JOJO
首先题意可以转化成求出 \(\sum next[i]\)。
先考虑没有操作 \(2\) 怎么做,如果我们把二元组 \((x, c)\) 看成一个新的字符,那么实际上这个问题就变成了一个 \(kmp\) 问题。可以这么做:考虑新加入一个字符段 \((x, c)\),我们暴力跳前面元素的 \(nxt\),直到找到一个 前缀,其往后的字符段的字符为 \(c\),那么假设这个字符段的长度为 \(x'\),如果 \(x'>x\),那么整个区间的答案就已经确定了,是一段首项为当前未被覆盖长度,公差为 \(-1\) 的等差数列,如果 \(x'<x\),那么有 \(x'\) 个的答案也已经确定了,仍然是一段长度为当前未被长度。至于 \(nxt\) 的更新,必须要找到一个恰好为 \((x, c)\) 的字符段进行更新。
考虑加上 \(2\) 操作怎么做,由于 \(kmp\) 的复杂度并不严格,而是均摊的复杂度,所以直接可持久化是有问题的。考虑一个神仙优化:如果 \(nxt[i] < i/2\),那么我们直接跳是没有问题的,否则的话,当前字符串一定存在一个周期,那么我们暴力跳一个,判断之后直接跳到第一个周期去,这样长度一定会除以二,所以复杂度正确。
[HNOI2019] 多边形
首先不难发现,每一步一定会有一个新的点连向 \(N\) 号点,所以第一问的答案就是初始时没有连向 \(N\) 号点的节点数量。
考虑一条边 \((i, N)\),它会将原图分成两个部分,这两个部分互不影响。那么我们求出每一个部分的答案,然后乘以组合数,即可求出方案数。
对于每一部分,它将对应一个区间 \([l, r]\),不难得出 \((l, r)\) 一定存在一条连边,那么我们第一步一定是选择一个点 \(x\),使得 \((x, l), (x, r)\) 分别有连边,然后断开 \((l, r)\),连接 \((n, x)\),此时区间分裂成 \((l, x), (x, r)\),又变成了子问题,这会形成一颗树的结构,我们只需要求出这个树的合法拓扑序即可,组合数即可轻松解决。
剩下两个问题,第一个是如何快速找到 \(x\),第二个是如何快速维护修改。
找到 \(x\) 很简单,直接找到与 \(l\) 相连的,最大的小于 \(r\) 的节点即可,可以直接二分。
考虑如何快速维护修改,手玩一下即可发现是一个 \(rotate\) 的过程,修改一下对应的组合数贡献即可。
[HNOI2019] 校园旅行
首先有个比较好想的暴力:考虑回文串去掉收尾仍然是回文串,那么考虑设 \(dp[i][j]\) 表示 \([i, j]\) 是否为回文串,然后枚举两条出边转移即可,类似于 \(SPFA\),复杂度 \(O(m^2)\)。
考虑到路径是可以走重边的,那么我们只关心每一段 \(0/1\) 的奇偶性即可。我们单独考虑 \(0\to 0\) 的边和 \(1\to 1\) 的边,如果这张图是二分图,那么奇偶性无论如何也发生不了改变,只需要保留一颗生成树即可。如果不是二分图,那么就存在一个点可以改变奇偶性,只需要加一个自环即可(可以通过走到自环部分改变奇偶性之后再走回去即可)。
[HNOI2019] 序列
首先考虑 \(50pts\),我们可以直接上保序回归,也就是整体二分之后,枚举 \(mid\) 与 \(mid+1\) 的分界点,然后找出最大值即可。也有一个更直接的方法:
类似于 \(P4331\),考虑最后的序列一定是可以分成若干段区间,使得区间内所有数相等,且这段区间的所有数为本身这段区间的平均数。
考虑 \(\sum_{i}(A_i-x)^2\),我们需要支持快速合并。不难得出其为 \(\sum_{i=l}^rA_i^2-\dfrac{(\sum_{i=l}^rA_i)^2}{r-l+1}\),那么只需要维护区间平方和,区间和,区间长度即可快速合并两端区间。然后维护一个单调栈,每次新家一个元素,如果可以比单调栈栈顶的元素值小,那么必须不断合并,否则的话新开一个元素,我们可以 \(O(N)\) 处理每一个询问。
考虑到每次询问是独立的,考虑最终的答案一定是存在一个 \([L, R]\),使得区间 \([L, R]\) 的的平均数为 \(x\),然后 \([1, L-1], [R+1, N]\) 还是原来的情况。
考虑用主席树处理出每一个前缀对应的单调栈以及每一个后缀对应的单调栈。先假设每次修改是给 \(x\) 进行单点加操作。那么此时 \([1, x]\) 的单调栈仍然合法,且 \([x+1,n]\) 的单调栈仍然合法。如果是单点减操作,那么 \([1, x-1]\) 的单调栈仍然合法,且 \([x,n]\) 的单调栈仍然合法。
考虑给定两个单调栈如何计算答案,如果第一个单调栈的最大值小于第二个单调栈的最小值,那么我们已经可以计算出答案了。现在的问题是第一个单调栈最后一个元素可能大于第二个单调栈第一个元素。这个时候我们就需要将其合并。考虑原来的暴力,不难证明只保留单调栈的元素做暴力仍然是正确的。
设 \(v[L, R]\) 表示单调栈上 \(L, R\) 的加权平均数,\(v[x]\) 表示 单调栈上 \(x\) 的权值。
那么我们需要合并两个单调栈,就需要找到一个 \([L, R]\),满足 \(v[L-1]<v[L, R]<v[R]\)。
假设将 \(R\) 固定了,考虑找到一个 \(L\),满足 \(v[L-1]<v[L, R]\) 怎么做。假设 \(L\) 是合法的,即满足 \(v[L]<v[L+1, R]\),又因为在单调栈上 \(v[L-1]<v[L]\),所以 \(v[L-1]<v[L, R]\),所以 \(L\) 存在单调性。同理 \(R\) 也存在单调性。
由于这是单调栈,注意到 \(L\) 在满足 \(v[L-1]<v[L, R]\) 时,\(L\) 越大 \(v[L, R]\) 越大,所以我们需要二分找到最大的合法的 \(L\),才能使得 \(v[L, R]\) 最大。对于一个 \(R\),找到了以它为右端点最大的 \(L\) 之后,只需要判断 \(\max(v[L, R])\) 与 \(v[R + 1]\) 的大小关系即可。
复杂度 \(O(nlog^2n)\)。