日记 2024.3.15:2024 年 syzx 春季训练 1

日记 2024.3.15:2024 年 syzx 春季训练 1

https://vjudge.net.cn/contest/615820

题号 1/2 是两种做法,不是两个题。

A

找出在 \(1, 2\) 周围一圈的点,挑出最远点 \(u, v\)(找不到说明 \(d_{1, 2}=1\)),判一下 \(d_{u, v}\)\(d_{u, 2}\) 的关系以区分 \(\pm1\)。这样比较好看。

B

普通冒泡 \(n(n-1)/2\) 次,这题 \(n^2\),说明每做一次操作可以浪费一次操作。

\(4\cdots n\) 普通冒泡,动不了就搞 \(1, 2\)。最后只剩三个数,一共六种情况,经验证,直接做都对。

C

枚举一个学生,枚举这人回答的问题,枚举和他回答相同的学生暴力验证。\(O(n^2m)\)

发现,枚举一个学生后,如果枚举了超过 \(O(nk)\) 个学生,说明已经有一个人和你相似了(否则鸽巢原理)。然后再去枚举与这个学生相似的,暴力验证。\(O(n^2k+nm)\)

D1

显然有 \(O(nd(n)\log n)\) 的做法。考虑你这个修改,可以拆成前后缀去询问,那个 \(\log n\) 就能精细实现干掉。

好厉害,\(\max_{n\leq 10^6}d(n)\) 竟然是 \(200\)。还以为根号。

D2

\(k\) 必定是 \(n\) 的某个素因子。

你想象一下。已经有一个 \(k\),再次划分之。

\[\dfrac{S'_{\max}}{S'_{\min}}\leq \dfrac{aS_{\max}}{aS_{\min}}=\dfrac{S_{\max}}{S_{\min}} \]

(新的最大值所在的组,\(\leq\) 缩放成全部都是原来的最大值;最小值同理;然后除掉一个同样的个数。最终发现比原来还优(不劣)。)

于是 \(O(nw(n))\) 做法就出现了。


以下四个题是连续的。

E

你要想象一种 xor-shift-\(k\) 的操作,使得数组中所有下标为 \(i\)\(i\oplus k\) 的地方互相交换。

就是线段树向下的时候,如果这一层被异或了,左右儿子交换,要查询的区间也交换一下。

00000111|22220000
交换为
22220000|00000111

询问真正的左右儿子的这两段前后缀。可以篡改询问区间,但是我喜欢篡改管辖区间。

复杂度 \(O(q\log n)\) 别写假了。彭老师在每个线段树区间上树状数组询问,吓到我了。

F

操作二:xor-shift-\(2^k\)

操作三:xor-shift-\((2^k-1)\)

xor-shift-\(k\) 是可以叠加的,直接 xor 起来。然后操作四就和上题一样了。

G1

xor-segtree。是新的科技啊。与省选 D2T1 一样深刻。

线段树的每一个线段上存 \(dp\) 数组,长度为 \(len\)\(dp[i]\) 表示这个子树内做 xor-shift-\(i\) 之后的最大子段和信息(叫作 info)。然后在一个线段上,已知左右儿子的 \(dp\) 后,\(<2^{k-1}\) 的部分是左儿子对应 merge 右儿子的 info,\(\geq 2^{k-1}\) 的部分恰是右儿子对应 merge 左儿子的 info。然后全对了。

G2

按照格雷码顺序,根据格雷码那题的构造,每一位上被 swap 的次数是 \(2^k\) 这样,那就对着线段树上做 xor-shift-\(2^k\) 的代价对应乘过去均摊到 \(O(n\log n)\) 次修改。实际上就是倒着构造格雷码,优化枚举顺序。

总结:两个做法都非常深刻,是两种不同的思路。第一种的修改是线性的,第二种只能重构。

H

沿用 G1 做法,info 改为哈希值。太好写,太优美。线段树套线段树,\(O(2^nn)\) 带走。

据说可以反向刻画成后缀数组建法,避开 hash。就是自底向上地,把刚才做法的 hash 值改成这个 \(dp\) 数组里面的相对排名。(我觉得应该是整一层的相对排名。)然后发现可以拼起来继续比较。然后复杂度好像不如上面的。

沿用 G2 做法会不幸多一个可持久化线段树。

关于修改,就是那些位置无关或者位置粗糙相关的东西可以打 lazy-tag。有点太粗糙了。

I

https://www.cnblogs.com/caijianhong/p/18296748/solution-AGC048D

J:CF1938L

统一概念

一个 0/1 向量,很长的称为 bitset,短一点的是 unsigned int,统称为 bignum。一个 bignum 集合的线性基集合是它的子集,线性基集合中的数字可以异或出原集合的所有数。线性基是一个程序中黑盒子,支持维护一个 bignum 集合,操作是插入一个 bignum,回答这个 bignum 在插入之前是否能被原来的线性基集合表示。

Part 1

\(b\) 拍平。即是问,有一个长度为 \(30n\) 的 bitset,还有 \(\dbinom n 2\) 组元素,每一个是 \((i, j)\) 对应的 bitset,这个 bitset 只在 \((30(i-1), 30i]\)\((30(j-1), 30j]\) 这一共 \(60\) 个位置有值。求线性基大小。但是 \(n\leq 10^5\),四次方暴力还是太极端了,但是能做了。

Part 2

我们将要做的第二个转化将要将答案写为 \(2^{cnt}\) 的形式:

int cnt = 0;
for (int i = 1; i <= n; i++) {
  for (int j = i + 1; j <= n; j++) {
    bool f1 = bas[i].insert(a[i] xor a[j]);
    bool f2 = bas[j].insert(a[i] xor a[j]);
    cnt += f1 || f2;
  }
}

其中 \(bas[1...n]\)\(n\) 个线性基,\(bas[i]\) 表示 \((30(i-1), 30i]\) 这些主元上的线性基情况,我们仔细想一下这个线性基的线性基集合只需要关注主元那 \(30\) 位。因为假如你认为在 \(bas[i]\) 里面洗了一遍之后后面的位置会影响 \(bas[j]\) 或者其它的地方的能否插入,那我们发现:

  1. 如果你认为会影响 \(bas[j]\),那么请问你在 \(j\) 的主元怎么来的?根本没有来源。
  2. 如果你认为会影响其它地方的主元,比如影响 \((30(k-1), 30k]\),那么这个地方的主元,你都拿到了,你之前是不是插入过了 \((i, k)\)?如果 \((i, k)\) 插入成功了,你就没机会再插入了;否则你也会失败。

所以这个算法就对完了。

Part 3

考虑到 \(cnt\leq 30n\),我们需要枚举 \(O(n^2)\) 次,有很多枚举是无用的!考虑这么个事情:

  1. \(a'_{i}=a_i+2^{30}\),对 \(a'\) 求线性基集合。
  2. 将线性基集合中原来的 \(a\) 调到 \(a\) 前面。
  3. \(j\)\(1\) 枚举到 \(n\)\(i\)\(1\) 枚举到 \(\min(31, j-1)\),然后做 part 2。

它怎么个对呢?考虑到后面的没有枚举到的 \((i, j)\),此时 \(bas[j]\) 已经被喂了 \(31\) 个线性基集合的线性基,满了插不进去;\(bas[i]\) 之前在成为 \(j\) 的时候,也被喂饱了,也插不进去。于是算法就正确了。

总复杂度 \(O(30^2n)\)。能过。

posted @ 2024-03-15 20:19  caijianhong  阅读(62)  评论(0编辑  收藏  举报