杂题记录1
P1360 [USACO07MAR] Gold Balanced Lineup G
咋一看挺难转化为一个有效状态供后面查询的。这里有两种思路可以引导至正解。
-
最朴素的列式子,\(sum_{i,k_{1}}-sum_{j,k1}=sum_{i,k2}-sum_{j,k2}=......\) 的时候方能满足要求,显然应将同一天的放在一起变为一个状态,所以移项 \(\to\) \(sum_{i,k_{1}}-sum_{i,k_{x}}=sum_{j,k_{1}}-sum_{1,k_{x}}\) 到这里就很显然了我们要维护 \(n-1\) 个差分。
-
如果思路更顺一点的话可以想到,均衡生长的意思不就是相对能力值没有变嘛,所以直接差分。
实现的话可以暴力维护 map 里面的 \(key\) 类型为 vector
CF360E Levko and Game
既然是让一个人优于另一个人的,所以首先可以观察到其实可变边权只有选 \(l\) 或者 \(r\) 才更有意义。
其实可以动态设置边权,在 \(s_{1}\) 先到的时候设置为 \(l\),在 \(s_{2}\) 先到的时候设置为 \(r\)。可以证明正确性,即这么根据一个人设置不会影响到另一个人,假设 \(s_{1}\) 先到 \(u\) 后,将边权设置为 \(l\),如果 \(s_{2}\) 后续也到了这个点,那么二者后面的最短路径应该是重合的,\(s_{1}\) 先到这个点,故 \(s_{1}\) 先到终点。
这里要注意细节,这也关系到了本题的 hack,如果二者同时到达怎么办?比如你优先队列先弹出 \(s_{2}\) 然后你将边权设置为了 \(r\),但是如果 \(s2\) 有另一条短路他可以走那一条,可是这个 \(r\) 可能就影响到了同时到达的 \(s1\) 导致他后续最短路可能变长。综上,我们改变一下算法,进行两次 Dijkstra,其中第一次先争取赢,如果不行再争取平局。第一次策略如上,第二次在上一次基础上稍微改变策略,如果二者同时到达,将边权改为 \(l\)。
AT_arc066_c [ARC066E] Addition and Subtraction Hard
好题啊,单纯去想括号的几层嵌套非常难搞,其实可以从最优化角度去想。考虑什么加括号目的,显然是要最小化第一层括号内的值,最大化第二层括号内的值。
那就是让第一层括号内的减号尽可能多,第二层括号内的加号尽可能多。此时再加第三层括号显然是没有意义的。这是一个贪心的思路,我们可以用 \(dp\) 来实现它。
P8445 射命丸文的取材之旅
注意观察 \(a_{i}\)和 \(b_{i}\) 范围,所以选择枚举 \(mex\) 的值。考虑到一个值为\(mex\)的必要条件,只有当该区间没有这个值的时候才行,同时注意到 \(mex\) 特征以及题目要求,发现就算我们算大了 \(mex\) 也不会使得答案不对。 所以在区间没有该值的时候直接更新答案。\(a_{i}\) 与 \(b_{i}\) 只有相等才会产生限制。
P8817 [CSP-S 2022] 假期计划
两种做法。
-
随机化骗分。考虑为什么不能直接 \(dp\) ,因为可能会重复旅游节点,发现如果给每个节点赋上一个阶段就不会重复了。于是建立满足距离不超过 \(k\) 的图,然后在上面随机染色,也就是说每个点被赋予了一个第几个到达的随机阶段,然后根据阶段跑一遍 \(dp\)。正确性验证: 一次正确的可能性为 $ \frac{2}{4^4}$ ,即最后答案的四个点,每个点都有4种阶段可能。跑个200次就差不多了,可以用卡时函数。
-
不会就暴力再优化,最暴力的是枚举四个点,考虑优化这个过程。一般这种多个枚举的优化都是枚举部分再拿数据结构之类匹配剩余的。本题其实不太好匹配,为了更方便,观察到第一个点和第四个点是对称的,于是枚举中间两个点,用 \(set\) 匹配即可。
ABC128F Frog Jump
最暴力的思路就是枚举 \(A\) 和 \(B\),然后统计。复杂为 \(O(n^2 \log n)\)。一般来说为了简化计算可以只枚举其中一个端点剩下一个端点可以放在一起计算减少重复计算。这题不太方便这么做,但是思路也是类似的。可以画一下图,模拟一下跳的过程。然后发现 \(A-B\) 为一个周期。注意点的分类,奇数步为从终点开始的 \(A-B\) 往回眺,偶数步为从起点开始的 \(A-B\) 往前跳,于是就可以列出答案式子
发现这次枚举的是 \(A-B\) 就可以了,而且这样子每个答案计算式子中就有重合部分了可以前缀和减少重复计算,因为 \(A\) 的不同决定了第 \(k\) 个周期后跳到终点,于是 \(k\) 取任意一个合法的数都可以对答案产生贡献。
CF389A Fox and Number Game
虽然是红题,但是挺巧妙的。
发现到题目的要求本质是为错位相减,于是每个数最小变成整个数列的 \(\gcd\)
P3129 [USACO15DEC] High Card Low Card P
显然不能直接枚举断点计算,于是可以记录前后缀然后拼接在一起。因为一开始是数字大赢,后面是数字小赢,所以我们可以贪心地将大的卡牌在前面出,小的卡牌在后面出。当规则为最大赢的时候,如果当前最大数字也无法满足,那么根据田忌赛马我们应该选择最小的数字来匹配。
可是我们并没有枚举断点,不知道当前可用的最小的数字是哪个,比如如果当前选择了 \(1\) ,但是在后半段的拼接中又用到了 \(1\) 那就重复使用了。大胆猜测可以通过微调使得成立,如果假如重复使用了 \(x\) ,那么必定存在一个相异的 \(y\) 。根据 \(y\) 与 \(x\) 的大小关系在前缀或者后缀中替换 \(y\) 就可以了。
P9013 [USACO23JAN] Find and Replace S
看到这题的第一想法肯定是一堆字符串变来变去,非常复杂。此时不妨冷静思考一下突破口:显然对于一个字符 \(s\) 它只能变成除了 \(s\) 之外的。约束关系考虑建图。这个时候需要分类讨论一下:链直接是边数。环的话需要引入环外一点(如果不存在额外一点就无解),答案是边数加一。基环树答案也是边数。
CF163D Large Refrigerator
搜索题。
先确定思路,应该是逐一确定 \(abc\)。然后对于要搜的数考虑 \(V\) 的每一个质因子应该放几次方进去。
接下来就是剪枝
-
可行性剪枝,不妨设 \(a \le b \le c\),那么 \(a\le\sqrt[3]{V}\) 且 \(b^2 \le \frac{V}{a}\)。
-
优化搜索顺序,发现 \(abc\) 三个数尽可能平均时答案应该会更大。所以 \(a\) 作为最小的数应该尽可能大一点才能保证平均,这样才会先搜到更优答案以方便后续更快排序不优的答案。同理确定 \(a\) 后 \(b\) 也是如此,于是我们需要对于每一个质因子从最大的指数开始枚举。
-
最优性剪枝,
固定 \(a\),
考虑面积表达式,
如果当前搜到的 \(a\) 所得面积下限大于当前最优面积那么直接剪掉。
点击查看代码
void dfs2(long long num1,long long num2,int tot){
if((double)num2*num2>v/num1) return ;
else if(tot>k){
long long num3=v/num1/num2;
if(num1*num3+num1*num2+num2*num3<ans){
ans=num1*num3+num1*num2+num2*num3;
a=num1;
b=num2;
c=num3;
}
}else{
for(register int i=cnt[tot];i>=0;i--){
cnt[tot]-=i;
dfs2(num1,num2*p[tot][i],tot+1);
cnt[tot]+=i;
}
}
}
void dfs1(int tot,long long num){
if((double)num*num>v/num) return ;
else if(tot>k){
long long x=v/num;
if(ans<=2*num*sqrt(x)+x) return ;
dfs2(num,1,1);
}else{
for(register int i=cnt[tot];i>=0;i--){
cnt[tot]-=i;
dfs1(tot+1,num*p[tot][i]);
cnt[tot]+=i;
}
}
}
void inp(){
cin>>k; v=1;
for(register int i=1;i<=k;i++){
scanf("%lld%d",&p[i][1],&cnt[i]);
p[i][0]=1;
for(register int j=1;j<=cnt[i];j++) p[i][j]=p[i][j-1]*p[i][1];
v*=p[i][cnt[i]];
}
ans=1e19;
}
dfs1(1,1);
CF1393D Rarity and New Dress
观察到一个 \(n\) 阶菱形必然由以它中心相邻的 \(4\) 个格子为中心的 \(4\) 个 \(n-1\) 阶菱形拼成,这启发我们设 \(dp_{i,j,k}\) 表示中心为 \((i,j)\) 的格子能否产生 \(k\) 阶菱形,该状态为 \(1\) 的条件是周围四个格子与之同色,且周围四个格子能产生 \(4\) 个 \(k-1\) 阶的菱形。这显然会超时。解决方案:
- 可以 bitset 乱搞。
- 一般遇到 bool 类型的 dp,如果某一维度具有单调性,也就是说越大越好。这里显然就是因为 \(k\) 大的时候满足,那么小的时候必然也满足。这个时候我们可以将状态中的 \(k\) 提出来,放到 dp 值里面,这样只需要最大化 \(dp_{i,j}\) 即可。两种处理方式,第一种就是每个点能向 \(4\) 个方向扩展的距离取 \(\min\),这时也不需要 dp 了,循环判断即可。第二种还是 dp,我们发现如果设置中心点的话会有后效性,因为 \((i,j)\) 依赖于 \((i-1,j)~(i,j-1)~(i+1,j)~(i,j+1)\),刚刚因为有 \(k\) 的阶段所以无后效性,现在去掉 \(k\) 后就有了。于是我们直接选取菱形最右边的点就行。
CF1348E Phoenix and Berries
这题的突破口就是寻找上下界,可以发现答案上界为 \(\lfloor \frac{\sum a_i+\sum b_i}{k} \rfloor\),下界为 \(\lfloor\frac{\sum a_i}{k}\rfloor+\lfloor\frac{\sum b_i}{k}\rfloor\)。不难发现二者最大差为 \(1\),且取决于 \(ra=\sum a_i \bmod k\),\(rb=\sum b_i \bmod k\),如果 \(ra+rb<k\),那么上下界相等直接输出即可。如果 \(k \le ra+rb <2k\),那么我们可以尝试用同一颗树上的果子来装篮子,使得采用这个方法用掉的 \(a~b\) 满足 \(ua' \bmod k \in (0,ra)\),\(ub' \bmod k \in (0,rb)\),且 $ ua'+ ub' \equiv 0 (\bmod k)$,如果此时进行 dp 的话状态数太大,我们发现 \(ua~ub\) 有约束关系,那么能否使用其中一个来表示另一个。 其实是可以的,只需满足 \(ua' \bmod k \in (k-rb,ra)\) 即可。
CF1367F2 Flying Sort
先看简单版,其实我们发现答案下阶是序列长度减去最长上升子序列长度,能否达到呢,因为我们移动数字的顺序是可以自己选择的,所以我们必然可以满足除了最长上升子序列外剩下数字可以在下阶操作次数下完成。然后是困难版,因为数字可重复了这就麻烦了,如果形如 \(x-1~x~x+1~x\) 这样的形式,虽然前三个可以构成上升子序列,但是我们无法把最后一个 \(x\) 插入进去,因此简单版的做法在困难版不可取。可以发现这里要求的新最长上升子序列中的每一项(除了第一和最后一项)必须保证所有与之相同的数字都在该序列里出现了。开头和结尾的出现几个无所谓。
于是我们设置出状态 \(dp(i,j)\),其中 \(j\) 取 \(0/1/2\) 分别表示 \(a_i\) 作为序列的第一项,中间项和最后一项时序列的最长长度。其中中间项的要求必须是 \(i=end_{ai}\)。 然后记录上次出现的位置转移即可。
P3515 [POI2011] Lightning Conductor
此处主要记录不用决策单调性的做法。
- 我们发现根号的取值是 \(O(\sqrt{n})\) 级别的。于是在每一个位置枚举根号取值然后在对应前后缀中查询 \(a_j\) 最值,这样算法是 \(O(n\sqrt{n})\) 的。
- 使用贡献法,对于每一个位置 \(i\) 考虑对别的位置的贡献,只需要在每一个根号值发生变化的地方打上标记即可。
- 优化:我们发现一个位置能对之后产生贡献的必要条件为这个位置的值大于之前的所有位置。这么一来在随机数据的情况下复杂度又下降为 \(O(n+\sqrt{n}\log{n})\)。
- 其实能产生贡献的位置更少,设序列最大值为 \(max\),那么只有值域落在 \([max-\sqrt{n},max]\) 之间的数才能产生贡献。而且该数必须是第一次出现。这就把能产生贡献的规模降到了 \(O(\sqrt{n})\),总复杂度为 \(O(n+\sqrt{n})\)。
[PR #1] 删数
其实数列是可以从左往右按顺序删除的,我们发现每次删除保留的是两边的数,也就是一个区间删完之后剩下的是左右端点,所以并不能通过左右各删一部分,使得左右边的某个数跨过了屏障相遇。这题还有区间平均数的不变性,所有剩下两个端点平均数为区间平均数不过好像没用。
部分分 \(a_i=i\),直接奇偶删即可。部分分,\(a_i \le 3\) 挺有意思的,其实我们发现本来是不能遇到能删就删,但是这个部分分策略就是能删就删,因为如果有连续的三个不同的数可以被操作,也就是 \(1~2~3\),我们发现左或右边的数无法被更左或更右的数删,因为它们已经是极值了。
部分分 \(n \le3000\),其实虽然 \(\sum n \le 10000\),但是单组数据 \(n\) 并不大,所有可以 \(O(n^2\log n)\)。易知区间删到最后就是两个端点,所以区间结果具有唯一性。于是设 \(dp_{l,r}\) 表示区间 \([l,r]\) 能否被操作,中间的分治点需要满足 \(a_{m}=\frac{a_l+a_r}{2}\)。观察性质,发现区间 \([l,r]\) 是单调的,二分寻找即可。
可以联想 NOIP2022 T3,正解是考虑差分,这样就是合并相同差分并乘以 \(2\)。固定右端点,发现可以满足条件的左端点级别在 \(O(\log n)\)。这里有一个小技巧,我们发现呈 \(2\) 倍关系的值 \(\frac{x}{lowbit(x)}\) 相同。
ARC067F Yakiniku Restaurants
法一:设 \(f_i\) 表示 \(i\) 的转移点,我们发现 \(f_i\) 具有单调性,分治即可。
法二:差分其实还有标记功能,对于我们用 \(s_{l,r}\) 表示 \([l,r]\) 的贡献,对于每一种烧烤券,每家烧烤店都有一个左右的控制范围,在那个范围内对于该烧烤券必然选择该店,假设为 \([l,r]\) 于是对于左端点位于 \([l,i]\) 且右端点位于 \([i,r]\) 的决策必然选择该店。于是二维差分标记一下即可。
CF1943E2 MEX Game 2
像这种求 \(mex\) 的题目一般都是枚举 \(mex\) 判断是否可行。本题可以将枚举改为二分,然后就是需要判断能否 Alice 无法取到 \([1,x]\) 内的所有数。
明确一下双方的策略, Alice 肯定是先取小的。考虑这么一种情形,就是 Bob 将全部 \(k\) 次花在了第 \(i\) 堆上但是没拿完,那么 Alice 一下将第 \(i\) 堆删掉,以此类推,这种显然 Bob 的努力也就白费了,所有正确的策略应该是均匀地取 \([suf_x,x]\) 之内的数。于是需要保证当 Alice 取到 \(suf_x\) 的时候,\([suf_x,x]\) 之间的极差小于等于 \(1\)。这个位置显然是可以二分的。找到这个位置之后如何操作呢,我们发现此后的每个位置基本上是等价的,于是只需要求出总和 \(sum\) 和剩余个数 \(res\) 就行了,然后根据 \(Alice\) 策略每次 \(sum \to sum-\lfloor\frac{sum}{res}\rfloor\),然后 \(res \to res-1\),同时 \(sum \to sum-k\)。如何快速判断 \((sum,res)\) 是否符合要求呢,有个小技巧可以预处理递推数组 \(dp_i\) 表示剩余 \(i\) 个的时候能满足的最大 \(sum\)。
CF201E Thoroughly Bureaucratic Organization
可以得出最后的排列当且仅当每个位置 \(i\) 对应的询问集合互不相同。
考虑二分答案次数 \(k\) 转化为判定,于是我们就需要找到 \(n\) 个 \(k\) 位的互不相同的二进制串,使得任意一位的 \(1\) 总数不超过 \(m\)。这里需要大胆猜想一下,只需要总数不超过 \(mk\) 就行了,至于证明就是考虑某一位 \(i\) 个数大于 \(m\) 个,那么不断移动到小于 \(m\) 的位置 \(j\) 上,那么会不会有冲突呢,也就是移过去之后发现有一个数其他位相同且正好也是 \(i\) 无,\(j\) 有。可是因为 \(i\) 的数比 \(j\) 多,所以总能找到一种搭配使得与 \(j\) 不冲突。然后只需要贪心地从 \(1\) 个 \(1\) 开始放就行了。
CF333E Summer Earnings
转化一下题意就是选择三个点,使得它们构成三边的最小值最大。
直接枚举三边是 \(O(n^3)\) 的,于是我们可以先将边降序排序,发现一条边可以产生贡献,当且仅当它所连两点在此前都和另一个点连通过了(这说明这两条边比这条边大)。这可以用 bitset 判断。
CF878D Magic Breeding
神仙题...
发现每次需要维护处理的信息量是 \(O(n)\) 的,难以通过数据结构之类的操作。
同时注意到每一种特征是独立的,而该特征的取值只有不超过 \(12\) 种,这启发我们对于取值进行操作。
首先对于每种特征的取值离散化,排名为 \(k\) 的取值的 \(1-k\) 位为 \(0\),\(k+1-12\) 位为 \(0\),于是取 \(\min\) 就变成了按位与,取 \(\max\) 变成了按位或。
此时还是有点难以维护 \(n \times k\) 列的 bitset,我们发现由于是 \(0/1\) 串,于是最多存在 \(2^k\) 种本质不同的列。
对于每个物品,我们只需要维护其 \(2^k\) 种列的变化(无论他有没有这种列)即可。
P10308 「Cfz Round 2」Osmanthus
如果打表需要打不同类型的表找规律,而不是对着某一个地方死打,这题的思路也是的,两种思路,拆位走不通就走 \(x~xor~x=0\)。后者是可以的,我们回归需要异或上同一个数抵消了,于是类似线段树结构分组,第一组搞完了,异或一遍才能消掉第二组的数,然后一二合并才能消掉后面的数。于是答案就是 \(2^{\lceil logn \rceil}\) ,注意这里的 \(n\) 是去除前导 \(0\) 后的序列长度。
「NOIP 多校联考2019」修改权值
如果往树上某点的值往什么地方修改那么想法就偏了,因为会上下影响,这种题目要有一个意识就是调整是很自由的,一旦某些东西确定了,其他地方必然有一种方式满足。
需要善于转化其实就是树上 LIS,于是我们用 set 启发式合并,每次二分更新即可。
CF1503D Flip the Cards
首先一张卡片同时包含 \(\le n\) 或者 \(\ge n\) 显然不行。
这种题目还可以随意排序,有些混乱,于是我们考虑统一形式,先不管操作次数只去满足可行性。假设现在正面朝上的都是 $ \le n$,设反面是 \(f(i)\)。于是最后肯定是前面一些 \(i\),后面一些 \(f(i)\)。
其实就等价于按 \(i\) 排序后的序列划分成两个 \(f(i)\) 单调递减的序列。
这可以通过开两个栈,每次贪心地放入栈顶较小地栈来完成。最后的答案统计就是看两个栈中翻转谁需要的代价下,就选谁。
那么如果最优化呢,如果要最优化不一定是放栈顶较小的,可能使得可行性劣一点能使答案更优。
序列问题有一种很典型的划分方式,就是根据最大最小值来划分。根据位子 \(i\) 满足 \(\min\limits_{1 \le j \le i} f(j)>\max\limits_{i<j \le n}f(i)\) 来划分,这样每一段显然是可以拼接的。注意到栈顶的两个元素 \(s_1\) 和 \(s_2\) 满足 \(\min(s1,s2)\) 为序列前缀最小值。当我们放入 \(f(i)\) 的时候,如果有\(s_1>f(i)\) 且 \(s_2>f(i)\),根据划分方式后面必然存在 \(f(j)>\min(s1,s2)\),如果用 \(f(i)\) 替换 \(\max(s1,s2)\) 必然会使得后面的 \(f_j\) 不满足要求,所以我们只能被强制要求放到较小栈了,这就唯一确定了。
CF1805F Survival of the Weakest
先是 easy 版本,队列求前 \(k\) 大是很简单的,暴力合并 \(F\) 即可。这里需要注意我们不能在中途取模,这样会改变相对大小关系,但是为了保持相对大小我们可以给序列同时减去 \(a_1\),在第 \(i\) 步我们减去的 \(a_1\),后面因为合并会被加上 \(a_1 \times 2^{n-1-i}\)。
然后是 hard 版本,打个表或者猜测一下,发现越到后面编号越大的位置能被用到的可能性越小。每次只保留前面一部分即可。
CF407D Largest Submatrix 3
很显然的暴力枚举左右边界,对于上下进行双指针,时间复杂度 \(O(n^4)\)。
思考一下复杂度花在哪里了,主要是扫描线每次推进的代价太大了,由于左右列间距很大,每次推动指针向下暴力扫描那一行的信息很浪费时间。考虑移动枚举左右边界的时候一点点移动继承上一次的一点信息。
仔细想想好像有点困难,虽然单纯继承前几列的信息很容易,但是加入新的一列面临的是很长一段新的信息如何整合?我们可以同时在每次下移下边界的时候,只计算新移动出来的这一行,然后对于上一行算出来的上边界结果取一下交即可。
总结一下就是我们维护 \(f_{h,l,r}\) 表示只考虑第 \(h\) 行,列区间在 \([l,r]\) 之内的最多能延生的上边界(上为开区间),行内更新 \(\max(f_{h,l+1,r},f_{h,l,r-1}) \to f_{h,l,r}\),同时我们可以发现对于 \(a_{h,l}\) 没有统计其在 \(r\) 列的情况(\(a_{h,r}\) 同理),于是我们维护 \(lst_{t,j}\) 表示对于数字 \(t\) 在第 \(j\) 列上一次出现的位置,然后 \(\max(lst_{a(h,l),r},lst_{a(h,r),l}) \to f_{h,l,r}\)。最后第 \(h\) 行作为下边界能取到最远上边界就是 \(\max(f_{h,l,r},\max\limits_{c=1}^{h-1}(f_{c,l,r}))\)。