2021.12.6 模拟赛

努力不咕。

T1

巨大多细节题,还不给大样例,我花了三个多小时写了三份代码(爆搜 + \(n^3\) + \(n^2\log n\))为了对拍。

首先没有字典序的限制的话,每行的贡献就是错排数。

逐位 DP,钦定一个前缀相等,当前位 \(<a_{i,j}\)

那么就要维护后面有多少个有限制的,多少个无限制的;\((i,j)\) 能填的有多少个有限制的,多少个无限制的。用树状数组之类的维护一下,细节很多。

假设后面有 \(a\) 个有限制,\(b\) 个无限制的,方案数二项式反演一下可以得到

\[f(a,b)=\sum_{i=0}^a(-1)^i\binom{a}{i}(a-i+b)! \]

,后面的行的贡献还是错排数。可以做到 \(\mathcal O(n^3)\)

拆一下式子,

\[f(a,b)=\sum_{i=0}^a\frac{(-1)^i}{i!}\frac{(a-i+b)!}{(a-i)!} \]

枚举 \(b\)\(n+1\) 次加法卷积,可以 \(\mathcal O(n^2\log n)\)

后来才知道这个东西可以直接递推,考虑最后一个(一定合法)放哪里:

\[f(a,b)=b\times f(a,b-1)+a\times f(a-1,b) \]

我真是太笨了。

T2

先写个不知道怎么算复杂度的假做法,目前被叉。

按权值从小到大考虑,将所有区间按照包含该权值的个数分类,找第 \(k\) 大所在分类递归下去。

具体实现的话,一个左端点所对应的所有右端点是按照包含关键点个数分段的,只需要每个左端点记录对应的右端点区间;按关键点划分区间,可以在位置集合上二分。

求教怎么算复杂度 /kel

std 正解解读:

\(k\) 大问题还是考虑二分答案,有几个问题:

  • 找什么样的区间作为关键值来划分?
  • 如何维护二分范围?
  • 如何快速比较每个区间和关键值?

肯定不能直接记录所有区间是什么。观察单调性,对于一个左端点 \(l\),区间的权值随 \(r\) 增大而增大。那么 \(l\) 所对应的合法区间集合,其右端点也是一个区间。

然后我们随机挑选一个区间作为关键值,容易理解二分次数依旧是 \(\mathcal O(\log n)\) 次。

注意为了均匀随机,也就是对于序列 \(a_{1\dots n}\),选中 \(a_i\) 的概率要是 \(\frac{a_i}{a_1+a_2+\dots a_n}\)

然后的问题是要划分当前的二分范围。假设我们得到 \(l\) 对应的最大的 \(r\) 使得区间 \([l,r]\) 不大于关键值,那么从 \(l\)\(l+1\)\(r\) 一定是单调不降的,双指针即可做到只用比较 \(\mathcal O(n)\) 次。

要快速比较比较每个区间和关键值。每个区间的权值可以记录为序列 \(c_{1\dots n}\),表示 \(i\) 权值出现 \(c_i\) 次,那么比较大小就是比较字典序。两个区间的权值作差,找到第一个不为 \(0\) 的位置即可比较大小,那么用 \(\texttt{std::set<>}\) 维护 \(c\) 中所有不为 \(0\) 的位置,双指针过程中增量修改,这部分复杂度 \(\mathcal O(n\log n)\)

总复杂度 \(\mathcal O(n\log^2 n)\)

T3

NOIP 前考过的原题,当时没改,今天就尴尬了。

具体来说横竖冲突的问题考虑最小割。先让所有点都选上最大的,算进答案,网络流的边流量就是最大值与当前点权值之差,求最小割。

将网格拆成两层图来限制单向的方向,两层图中对应的点用 \(+\infty\) 的边连接来限制不能同时被两者占有,边权就好说了。

posted @ 2021-12-06 15:05  RenaMoe  阅读(63)  评论(3编辑  收藏  举报