重庆 暑假 Day 1-4
Day 1
P12414 「YLLOI-R1-T3」一路向北
好题。“可以不再拿到 0”的条件比较难搞,我们发现条件是必须要有一行发生无限循环,否则总会有时拿到 0。
发现 \(i\) 行后面新插入的值只能是 \(i\),这很重要,当这一行全都是 \(m\) 个 \(i\) 时就会发生循环。
- 当 \(i\) 这个数在整个矩阵里也只有 \(<m\) 个,这时把 0 放在这一行一定弹不出来。显然。
- 当所有的 \(\forall i, cnt_i=m\),一定弹出来。
综上。判断是否 \(\forall i, cnt_i=m\),是就输出 No。
P13962 [ICPC 2023 Nanjing R] 电梯
结论神了。\(k\) 为偶数,把重量为 2 的包裹拆成两个重量为 1,答案不会变。剩下的直接贪。
先不管这个结论,可以这样,贪心 \(f\) 降序做,当剩下的重量为 1,当前是 2 的包裹放不下,直接从后面找一个最大的 1 填上。
注意到总代价和每一轮次的最大值有关,当出现放不下的 2 的时候下一轮的最大值没改变(和拆开后的贪心考虑),同时下一轮少了一个 1;但是如果下一个 1 不在下一轮,由于 \(k\) 为偶数,则下若干轮全是 2 而且放的满,也是相当于位移了一位,最大值没影响,所以解决。
CF1525C Robot Collisions
模拟,但是写法待改进。根据结论发现奇偶不同的完全独立,task 1(都不碰墙)括号匹配,task 2(碰一次墙)栈,task 3(都碰墙,方向反)也是栈,实际上写几个 vector 就行
CF1610E AmShZ and G.O.A.T.
简化问题,发现最强的性质(也即必要条件)是对于一个子序列三元组 \(\{x,y,z\}\),有 \(z-y\geq y-x\),那么,对于已知一个答案序列 \(a_1\dots a_{i}\),则有 \(a_{i+1}\geq a_i+a_i-a_1\),这就说明 \(a\) 的值是二的幂次级别上升的,这也就说明答案序列长度 \(\leq O(\log_2 V)\),大概是 30。里面二分的时候有个 \(\log\) 的复杂度,\(O(n\log V\log n)\)。
At_abc459_f -1, +1
来之前暑假的时候就补了,是下面的一道题的加强版
将严格上升改为严格不下降,然后尝试类似模拟,\(i:1\to n\) 枚举,考虑将当前答案序列分成块,每一个块已知总和和总长,那么值就是很多个 \(\lfloor\frac{sum}{len}\rfloor\) 到 \(\lceil\frac{sum}{len}\rceil\),每次枚举加入一个新的值(块),然后调整所有的块,将块丢进栈里,每次将两个栈顶拿出来比较,如果前一个的最大大于后一个的最小就合并一次,每次合并栈的 \(size-1\),所以最后复杂度 \(O(n)\)
CF1392F Omkar and Landslide
先把那个山全部减去一个斜率为 1 的直线,转换成上面那题的本质。
可以直接套用上一题的做法。
CF436E Cardboard Box
比较复杂,可以用 dp + 数据结构/决策单调性优化 或是反悔贪心。
已补。没那么麻烦,只用求一个 w 好做,老师上课讲的是求任意 w 的做法。
BZOJ3517 翻硬币
设每一个位置的操作次数为 \(x_{i,j}\),发现将一整行的所有 \(x\) 对应的 \(a\) 的异或方程 异或(本质上也是加和)在一起,就可以求出 \(\sum x\) 和 \(\sum_{j=1}^n x_{i,j}\) 的关系,同理可求得一整列的 \(x\) 总和,可以求得答案。
P8684 [蓝桥杯 2019 省 B] 灵能传输
发现是前缀和的交换操作。开头和结尾动不了,求最小的最大相邻前缀和差值。贪心,尽量让序列连续最好,对于 \(\min<s_0<s_n<\max\) 的要特殊考虑(钦定 \(s_0<s_n\),反过来也可以),考虑序列排完序后 \(s_0\to \min \to \max \to s_n\),对于值域上折返的地方(\(s_0\to \min\to(s_0)\),先减再增),发现贪心左右交替放值最优(一个放 \(s_0\) 下坡这边,下一个放 \(\max\) 上升这边),感性理解一下。
Rotate to Minimum
字典序最小,我们肯定是取尽可能长的一段前缀变成 \(\texttt{a}\),这个可以想到二分, 考虑 check。我们发现一个重要策略,就是,我们将二分的字符串的前 mid 个位置的字符拿出来,排好序,一定是前一段前缀用操作二,在这个位置之后的后缀用操作一,原因也比较好理解,排序后操作二变成 \(\texttt{a}\) 的代价递增,操作一的代价递减。
那什么时候转换,我们考虑到除了 1~mid 是 \(\texttt{a}\),mid+1 的位置也要尽可能小,而我们发现 mid+1 不可能是 \(\texttt{a}\),则我们尽量留下操作二最合适,所以我们将字符倒序排序,先尽量用完 \(p\) 的操作一,满足要求时留下尽可能多操作二,二分完把 mid+1 也修改了。
注意,到这里还没有结束,已知我们在 mid+1 用完了所有二,但是一没有,所以我们在 mid+1 后的位置也可以考虑用完操作一,当该位置到 \(\texttt{a}\) 所需操作够用的时候就变。
Day 2
A
- 发现所有的异或 \(2^k\) 可以搞在一起做
\(\text{xor}\ 2^0=1\): 值域上,相邻的值位置互换
\(\text{xor}\ 2^1=2\): [0,1]-[2,3] [4,5]-[6,7] 如此分组互换,组内相对顺序不变
- 发现在先异或完之后,这时再考虑交换,这时交换的操作次数就是逆序对个数。(有什么用呢)
- 发现神秘性质,对于序列,我们记录其 \(\text{xor}\ 2^k\) 之后逆序对的值从 \(pre\) 变化为 \(pre+f_k\),如果我们最终对序列异或了 \(x=2^{k_1}+2^{k_2}+\dots\),逆序对的变化后的值就是 \(pre+f_{k_1}+f_{k_2}+\dots\),也就是说逆序对的变化值是加和在一起的。那么我们为了让最终逆序对数最少,就将所有 \(f_k<0\) 的 \(2^k\) 添加到异或的 \(x\) 中,最后加上剩余的逆序对数
B
赛时想到了构造结论结果被一秒错误的口胡 hack 抛弃了
对于所有奇数的距离都连边,偶数都不连就行了。
原因可以这样考虑,对于偶数的点对一定不能连,而对于已经存在奇数距离的点对再多连边不会使答案更劣,于是连边一定不坏。
这样做还有一个好处,就是对于 \((u,v),dis_{u,v}\bmod2=0\),如果合法,则一定存在 \(w\),有 \((u,w),(w,v)\) 的连边(因为所有的奇数距离都连起来的,所以不需要两个长度大于 1 的奇数路径相连)。
判断不好做,赛时很多人直接写了 bfs,由于边数 \(O(n^2)\) 所以 bfs \(O(n^3)\) TLE 了。我们利用点 \(w\) 的性质,判断偶数距离 \((u,v)\) 时,将 \(u,v\) 相邻的点放到 bitset 中,两个 bitset 做与运算如果不为空则存在 w,这样可以加速 \(\frac{1}{w}\) 倍,解决。
C
组合数学,先需要转换题意,对于排序后的序列 \(a\),答案为 \(D+\sum_{i=2}^n \min(a_i-a_{i-1},D)\)。显然,因为如果相邻两位差距大于 \(D\) 就可以在这里断开产生新序列。
求答案时,我们求原数组的差分数组的方案,将差分分成 \(d_i<D,d_j\geq D\) 两部分做,枚举 \(d_j\geq D\) 的个数,使用插板法求答案。最后的公式比较复杂。
没补
D
P9447
E
p5972
有点考验思维和小 trick(ad-hoc?)。
最终数据范围很小可以发现。先从搜索开始考虑,再到状压 dp,但是状压 dp 没有实质性优化。继续发掘可以优化的性质,遍历到是否选 \(i\) 的位置,若后面剩下的元素集合为 \(\{p_1,p_2\dots p_m\}\),那么对于 \(i\) 之前的两个元素 \(p_j<x,y<p_j+1\),事实上这两个元素对于之后的状态搜索没有区别。那么我们将状压时,对于 \(x,y\) 这一类的位置状态信息改成在 \(p_j\to p_{j+1}\) 这段内有几个数,状态数就会大大降低,至于是多少呢,我们来算一下。
这个复杂度的上界的本质是,将 \(n\) 这个数分成若干段使得总和为 \(n\),这若干段的乘积最大。根据小学数学知识可得全分成若干个 3 最大,所以复杂度 \(O(n^23^{n/3})\),\(n^2\) 应该是 dp 的复杂度。
CF1567E Non-Decreasing Dilemma
奇怪了这题,我一开始想着将非递减转化为差分 \(\geq0\) 结果写了半天写不对,题解做法是直接维护每个区间的最左右的 \(a_i\) 值,这样先判断左右两区间的交界处的 \(a_i\) 大小决定能否接在一起即可,这么写不仅思维逻辑通顺而且好写跑得快。
trick:做题不要把问题搞复杂,就着原题的逻辑推最舒服。类似最大子段和的线段树合并,可以多记录信息,例如这题记录区间左右端点值并记录左右延续的连续不下降序列。
CF869E The Untended Antiquity
树套树+随机化,对我比较新的题型。
对于二维平面矩形覆盖,还要边修边查,排除扫描线可以想到树套树。考虑能否到达的本质是两个点是否在同一个连通块内,由于不会相交比较好处理,我们想到本质是需要让矩形内的点和外面的点标记不同,可以发现用异或哈希的方法可以做到,(我到这里没想到,想的是给每个点赋值 id,但是矩形内部包含的点没法处理)。这样的做法主要是从本质入手,给矩形内部的点全部操作,不会改变内部点的相对关系,但是恰好能改变内外部点的相对关系。(这种想法是忽视不重要的信息后简化问题)。
随机化+异或哈希(也可以使用加法哈希,本质相同)可以用来处理许多需要 标记 的题目。
发现我对树套树的熟悉程度不够。这题比较好写,使用树状数组套两层,原来的 for(;x<N;x+=x&-x) c[x]++ 加上一维
for(int i=x;i<=n;i+=i&-i)
for(int j=y;j<=m;j+=j&-j)
c[x][y]^=k;
这里绝不可以偷懒直接用 x,y 做循环要用 i,j 临时变量,这题 x,y 的值域都是 \(2e3\) 级别的,所以就可以这样简单地用二维数组。
这里提一嘴 hash 函数写法,将坐标位置 hash 了用 for(auto it : vec) hash = (hash * B) ^ it; hash = (hash ^ (hash >> M_SHIFT)); 做异或哈希,这样顺便利用 long long 自然溢出。
P5524 [Ynoi2012] NOIP2015 充满了希望
照着方法模拟肯定不太行。观察发现答案的特点,我们先考虑正常做 \(1\to m\) 完之后每一个 3 查询都会有一个值,设它为 \(norm_i\);对于某次 \(q\) 的查询中,一个 3 询问在这个查询的答案要么是 0 要么是他原来的 \(norm_i\)。这也好证明,考虑我们在第 \(t\) 次操作最后对 \(x\) 位置改变值,那么只要 \(q\) 查询的 \(l\leq t\) 那么该位置就有值。否则,我们发现,由于 \(p\) 之后的位置全部都不会对 \(x\) 赋值,所以这个条件是 \(x\) 有值的充要条件。
接下来考虑答案怎么求,对于一个区间 \([l,r]\),设我们要求的 3 查询 \(\{x,t\}\) 表示编号为 \(x\),最后修改的操作编号是 \(t\),则符合条件是 \(l\leq x\leq r,t\geq l\),是二维数点,而且不强制在线,所以可以按照 \(r\) 升序离线,树状数组维护下标为 \(t\) 的所有答案和。结束。
P11660 我终将成为你的倒影
神人百合厨题目背景,终将成为你,安达与岛村
不是这题怎么这么难补。分块题坐牢,一晚上只做了这一道。
md 卡常 1h 卡不完不搞了。
b 有点小,可以枚举每一个 b 预处理相邻位置哪些的 a 能够符合,具体来说就是分讨,只有 a%b 的值对 \(f\) 有影响,在枚举 b 时枚举这个余数,发现相邻位置有影响的 a 值是一个区间,这时做前缀和。预处理是 \(O(nb)\)(预处理可以前缀和加速 \(a\) 那一维),但是发现状态数总复杂度是 \(O(nb^2)\),有点炸。
考虑对 \(n\) 分块,这样我们存下来 \(f_{i,b,a}\) 表示 \(i\) 块中,查询求 \(\{a,b\}\) 时的答案,这样总复杂度变成 \(O(nb+b^2\frac{n}{B}+qB)\),块长为避免 MLE 并且加速取比根号大一点合适
历经 2 天千辛万苦的折磨,接下来是调试总结:
- 我一开始的枚举是先枚举 \(b\),再按照下标枚举求值,然后再加到该下标对应的块编号上,这样没有访存连续性是极慢的,由于这题需要以块编号为下标存变量,有这个前提导致了速度慢。正确的是枚举块编号,再枚举 \(b\) ,再枚举该块包含的所有下标,这样访存连续性高很多。
- 神秘 \(l+1\) 写成 \(l\) 导致 RE 一晚
- 差之毫厘谬以千里,如下
关掉 define int long long 效率翻倍
关掉 define int long long 效率翻倍
关掉 define int long long 效率翻倍
哦原来是这样,我关掉 define int long long 后 rep(i,X,Y) 里面的变量全都用 long long,例如 #define rep(i,X,Y) for (long long i=(X);i<=(long long)(Y);i++),所以关了和没关没区别(。
愿天堂没有分块 T_T
十分感谢 d 老师的帮助,没有它我无法取得最终的进展。
https://chat.deepseek.com/a/chat/s/27646277-2528-4c85-ac46-bd45e5623354
分块太折磨了,2h+1h 这到底谁受得了啊。同样的时间,比其它任何数据结构难吃 10 倍,相当于 30h 调试。。。
我已急哭
结语:『 现实并不像回忆那般,充满变化的余地。』
Day 3
P16858 [GKS 2021 #F] Festival
P7502 「HMOI R1」不知道是啥的垃圾题
trie 变体,发现 \(x\ \text{xor}\ a=y\ \text{xor}\ b \Leftrightarrow x\ \text{xor}\ y=a\ \text{xor}\ b\),所以可以按照 \(x\ \text{xor}\ y\) 建树,高位按照相同往下走。那不同的位怎么办?考虑一个位上 \(x,y\) 的四种情况分别哪些对应的 \(a,b\) 该位情况,状压成下标为 4 的数组就好。
警示: trie 的位数枚举别从 64 开始,是 UB 但是却显示 WA。deepseek 是真的强。
CF718C Sasha and Array
矩阵快速幂+线段树。
trick:为避免 TLE,在线段树的 lazytag 坚决不能写再加几层的快速幂,而要直接先求出来快速幂再以阶乘形式乘到 lazytag 上,否则有 \(O(\log)\) 常数加上。
P5953 [POI 2018] Różnorodność
真服了又是 1h 的卡常环节好在有大佬帮助了 https://www.luogu.com.cn/discuss/1338429
这卡的真厉害,只改了读入优化就过了,这快读 nb,也合理,读入 \(1e7\) 数据量真应该好好卡。czl 的什么垃圾快读。
说实话这回真是洛谷机器的问题了,QOJ 随便改随便过,https://qoj.ac/submission/2691977 一点不卡,算上之前在 qoj 做的题目前认为是最合我胃口的 OJ 平台了,在这做题真的舒服,哪都舒服,支持 qoj。
当然从代码层面继续优化也是可以的,有几个 trick (代码习惯)比较重要
(*it).x->it->x- 按照遍历顺序插入vector的时候不用sort
- 若没有该数就不用做很多操作
- 少开
long long
最后最大规模重写的办法是,将 ODT 代码的结构体 node 改成 pair<int,int>,这么做就能不用写 split 函数。具体要把 node 里的 l,r 只用保留 l,用作 first,区间的值继续保留,把 split 函数省去能优化时间。赛后交到 qoj 上发现有优化但不多,远远不如就学习这些优秀的习惯就可以小改动优化很多
感谢 YL_LiLuo_SK 大佬出手相助,下面 2. 也太高效了 5.20+s->4.66s
原因概括:set.erase(iterator) 远远快于 set.erase(value) !erase(value) 时应该会先二分找到在删,这样时间开销纯纯翻倍;而指针就不用二分。这里和上面的 1. 都体现指针的效率高超
谨记
主要就是两点吧喵喵:
- 用快读
node p=*prev(s[i].upper_bound({x,0,0}));
s[i].erase(p);
改成
auto it=prev(s.upper_bound({x,0,0}));
node p=*it;
s.erase(it);
做法讲解:还是很有趣的题。对于查询不同数个数我们发现是不好维护的,转换题意,先将查询一个矩形改为:对于每一个点,对它左上方向的矩形内所有询问的答案 +1,但同时同样的值对答案的贡献不重复计算。所以对于同样的值我们本质就是做矩阵面积并,这样就可以轻易求得总和。
至于最大值我们需要在扫描线的基础上增加一些修改,我们考虑由于所有正方形边长相等,所以当我们按照 x 扫描线时,我们新增的并到的面积可以以 y 为界限拆分成几个竖的长条(这里描述得很抽象,可以考虑就是新正方形的上部有一个轮廓线是已经有过的,我们新增的不能包括这些轮廓。这时我们考虑轮廓是如何生成的,发现是不同的正方形的下轮廓并在一起),
那么并完面积后这几个轮廓就都会没掉,被覆盖为新矩形的轮廓(这时可以想到,不同高度的轮廓连续段总数只有 \(O(n)\) 个,因为一次加入新矩形只覆盖一个连续段,而且每次清除的不会多余加入的 \(O(n)\) 个,所以这样用类似 ODT 的方式维护下轮廓的连续段就可以做到均摊 \(O(1)\times O(\log)\)。
记:这次集训我因为卡常调试浪费太多时间了,得考虑考虑怎么避免这种问题啊
CF1139F Dish Shopping
很典的二维数点,考虑建成坐标系 x 轴为钱,y 轴为味道,发现一个人所处在一个点上,而这时一道菜能覆盖的点集就是一个斜 45° 的等腰直角三角形,然后考虑将两条斜率不同的直角边分开考虑,从上直角边到 x 轴的这个梯形 +1,下边到坐标轴的梯形 -1 就可以维护这个二维数点了。
很想讲一下题解第一篇的代码非常优雅。很多时候你的代码常熟小并且好看只是因为用了很多小 trick,最终加起来就会有很好的效果。(本质还是需要你的思路清晰,脑子清醒,时刻清楚自己要在代码里干什么,就能自然地优化了。)
https://chat.deepseek.com/a/chat/s/b118ed1b-7858-445e-b625-4590df9d4837
- 二维数点的操作和查询可以用同一个数组,同时可以只用存 1.需要排序的关键字 2.所对应的原序列的下标 这样就会非常好写,做操作时用原下标上的值就很方便。
- 尽量少开变量,让一个类型有多个用处,没有本质区别的(不同斜率的操作就完全没必要分开,离散化数组,类似的)就别开太多。
P13780 「o.OI R2」愿天堂没有分块
https://www.luogu.com.cn/problem/P4137
https://www.luogu.com.cn/problem/P9970
http://oj.daimayuan.top/contest/406/problem/3362
⬆去年长训做的题,当时写了复盘题解,现在看这个做法并不是非常的先进,只是能满足题目的需求罢了,并不能很方便地求 极小 mex 区间。
从这题可以总结关键的 mex 题目的套路,一般都会先求出来 \(O(n)\) 个 极小 mex 区间,然后一般就变成区间问题或者是数点问题了。mex 典题。
预处理 极小 mex 区间 的方法可以用上面代码源那道题对应的 ODT 做法,但细节很多;另一种方法是,设我们求出了 \(x\text{-mex}\) 的所有极小区间,那么我们遍历每个区间找到该区间左边和右边第一个 \(x\) 的位置,这样左右分别扩展得到两个新区间,设新区间的 \(\text{mex}=x'\),将 \(x'\text{-mex}\) 的 极小 mex 区间 集合加上这个新区间。
CF2045E Narrower Passageway
https://chat.deepseek.com/share/a80z6ib2vo9lc9qsty
czl 教的用 deepseek 润色优化题解真的很强,而且专家也更厉害,因为专家思考更高效耗时反而少。
拆贡献,题目的式子拆成 \(max1+max2-[max1\geq max2]\times max1-[max2\geq max1]\times max2\)。继续拆,每种情况里中间的段的贡献是独立的。对于一个数单调栈分别求它为最大值,它是否大于等于另一行的对应最大值的情况,分别 +/- 它贡献即可。

浙公网安备 33010602011771号