杂题乱做3
补了一些讲过的远古题和近期的 CF 2000分以上的部分题。
CF1764H
题意:有序列 \(a_n\),初始 \(a_i=i\),给定 \(m\) 个修改操作 \([l_i,r_i]\),修改方式是把区间内所有数赋值成左端点的值。求修改操作在循环下,对于每一个 \(x\),依次进行操作 \([x,x+k-1]\) 之后序列不同数的个数。\(n,m\le 2\times 10^5\)。
首先对于没有覆盖的位置,扫描线时计算答案是简单的。对于覆盖的位置,只有每个询问的左端点可能产生贡献。
产生贡献的充要条件如下:
-
设上一个覆盖左端点 \(l_i\) 的操作是 \(f_i\),则显然当 \(x > f_i\) 时有贡献。
-
设 \(g_i\) 表示区间全被覆盖掉的最早时刻,\(v_x\) 表示 \(x\) 位置从当前开始最早什么时候会被覆盖,则有 \(g_i=\max_{k=l_i}^{r_i}(v_k)\),更新第 \(i\) 个修改区间的覆盖也比较简单。只需要将左端点的 \(v_{l_i}\) 赋成 \(g_i\),区间剩余部分赋成 \(i\) 即可。
CF1764G
有长度为 \(n\) 的排列,可以查询 \(query(l,r,k)\),交互库会告诉你 \(\lfloor \frac{a_i}{k} \rfloor,i\subset[l,r]\) 的不同值数量,在 30/21/20 次询问内确定 \(1\) 的位置,\(n\le 1000\)。
考虑将 \(2x+1,2x\) 看成一个 pair,考虑统计 \(f(l,r)\) 表示满足如下条件的 pair 个数:区间内部外部个包含一个值。
这个统计是 easy 的,因为你可以用 \(query(l,r,2)\) 算出来,满足条件的 pair 个数就是 \(2\times query(l,r,2)-(r-l+1)\)。
对于 \(n\) 是奇数的情况,判断 \(1\) 是否在某个区间内,只需查询区间内,外的满足条件 pair 个数,如果内部大于外部就是这个区间,否则就是二分的另一个区间包含 1。
对于 \(n\) 是偶数,发现 \(n\) 也特殊(无配对),所以只需确定 n 的位置即可。
通过 \(query(l,r,n),l<r\) 是否大于 1,可以判断出区间是否存在 \(n\)。
于是一种 naive 的做法是二分出 \(n\),但是发现这样不如玩原神。
其实可以正常二分过程中,发现左右区间的 pair 数都和外面相等了,就判一下 \(n\) 在哪个区间内即可。
这样就只需 21 次查询。
考虑再杀掉一次查询。
发现二分到区间长度等于二后,还需两次查询才能赢。
试图压缩成一次。
显然如果此时 \(n\) 还没有区分出来,那询问一次 \(n\) 在哪边即可。
其他情况,因为已知 \([1,l-1],[1,r],[l,n],[r+1,n]\) 的答案,通过分类讨论可以压缩一次查询。
设两个数中一个是 1,另一个是 \(x\)。
具体地,考虑设 \(g(l,r)\) 表示区间内 pair 的个数,那么显然我们二分过程中知道了 \(g(1,l-1),g(1,r),g(l,n),g(r+1,n)\) 的答案。
那么当 \(g(1,l-1)=g(1,r)\) 的时候,显然与 \(x\) 配对的另一个数在 \([1,l-1]\) 中。否则在 \([r+1,n]\) 中。
那么对于在左区间的的情况,查询一次 \([1,l]\),判断 \(g(1,l)\) 是否恰好等于 \(g(1,l-1)-1\),是则 \(r\) 是 1,否则 l 是 1。
在右区间的情况是类似的。
CF453E
显然颜色段均摊。首先用个 Set 维护颜色段。然后除掉 ODT,接下来的问题就是询问一段区间从 0 开始经过一段时间后的 sum。发现题目的 \(m\) 值域是 \(10^5\),所以分块之后对于每个块预处理下每个前缀和即可。
对于初值,可以在颜色是 0 的那些段暴力扫,是 \(O(n)\) 的。
CF1789D
傻逼构造。能不能s一s啊。简单题,但是位运算和垃圾的输出方式容易把人整晕。
设前面的位是高位。那么首先判掉一些显然的特殊情况,例如 \(a=b\) 无需操作,\(b=0\),\(a=0\) 显然无解。
剩余情况必然有解。考虑构造解。此时 \(a,b\) 都包含至少一个 \(1\)。
可以用 bitset 模拟操作。注意 bitset 操作时要随时把被 remove 掉的位干掉,避免影响答案。同时 bitset 的左移右移只能为非负。
首先设 \(mx1\) 是 \(a\) 的最高位,\(mx2\) 是 \(b\) 的最高位,那么如果 \(mx2\le mx1\),显然是容易构造出解的。只需先通过对 \(mx1\) 的左移异或(若 \(mx1=mx2\) 不用管)将 \(a\) 的 \(mx2\) 位变成1,然后从前往后按位考虑,当前的位和 \(b\) 不相同就将最高位 \(mx2\) 拉过来异或一下,否则不管。
那么就解决了 \(mx2\le mx1\) 的情况。
剩余情况 \(mx1 < mx2\),考虑转化成刚刚的情况。
此时如果 \(a\) 中只有一个 1,需要新开一个 1 方便操作,这个 1 要开在 \(mx2\) 的位置,这样 \(mx2\) 这位就省下了操作,保证总操作数不会大于 \(n\)。
然后取最低一位的 1,从 \(mx2-1\) 向最前面枚举,发现当前位是 \(1\) 就把最低位的 1 拉过来异或。
这样就消掉了高位的 1,转化到了上面的情况。
不难发现操作数严格不超过 \(n\)。
CF1789E
简单小数学题。
首先考虑 \(s_n\) 的因数 \(x\),这部分显然只有 \(x|s_i\) 的 \(i\) 贡献答案,枚举一下就好了。
然后考虑其他数。做过整除分块的都知道 \(\lfloor \frac{s_n}{x} \rfloor = y\) 的 \(y\) 一共只有 \(O(\sqrt{s_n})\) 种取值。
于是做一个整除分块。然后对于一个 \(y\),答案显然是满足 \(ay+b=s_i,b\le a\) 的 \(i\) 的个数。这部分可以预处理出值域上前缀和然后计算 \([py,py+p],p < y\) 的答案。而大于 \(y^2\) 的显然全部满足限制。
然后整除分块中可能把因数也算进去,枚举一下每个除数 \(x\) 看是否是因数,是就减掉他的答案即可。
复杂度不太会算,但是远比 \(O(\sum s_n \ln s_n)\) 小,应该是接近 \(O(\sum s_n)\) 的。
CF1789F
牛逼乱搞题。
类似于根号分治,我们考虑出现次数小于 5 的,显然这时只有出现两次和三次的是有用的(四次的被两次的覆盖了)。
那么枚举序列断点,求 LCS 即可。复杂度 \(O(n^5+n^3)\),而且常数极其极其小。
对于出现次数大于等于 5 的,发现如果把序列劈成五部分,则最优串一定是某部分的子序列。
这时每段长度是不超过 16 的,枚举子序列暴力全串匹配即可做到 \(O(5\times 2^{\frac{n}{5}}\times n)\)。
CF1796D
非常 easy 的 dp 题。
应该可以直接贪。但是当作贪心来做的话会需要分讨,比较麻烦。直接无脑 dp。设 \(f_{i,j}\) 表示考虑前 \(i\) 个数,已经选了 \(j\) 个数加 \(x\),转移直接枚举下一个数选不选即可。
更新答案找所有 \(f_{i,j}\) 满足 \(k-j\le n-i\) 即可。
CF1796E
首先,如果根是确定的,做法显然,只需要二分答案 \(x\) 就容易判断合法性。具体地,设 \(f_i\) 表示 \(i\) 子树内最短链的长度,则转移是 \(f_i=\min(f_v)\),其中 \(v\) 是 \(i\) 的儿子。
若有不小于两个 \(v\) 满足 \(f_v < x\),则直接不合法。
那么根不确定呢?当 1 不合法的时候,就是存在一个不合法的节点 \(u\),满足 \(u\) 至少有两个儿子 \(v1,v2\),使得 \(f_{v1},f_{v2} < x\),那么显然你必须以 \(v1,v2\) 最短那条链的叶子节点中的任意节点为新根,若都不合法就彻底摆烂。为什么是对的?因为你用其他子树的或者子树外的,则这两棵子树内的答案必然不变,仍然是寄的。
而如果根节点的 \(f\) 小于 \(x\),此时只需以其 \(f\) 值小于 \(x\) 的那个点对应的叶子节点检查一遍即可。
复杂度 \(O(n \log n)\)。
CF1795F
二分答案+树上贪心。
首先这种不好直接求的东西,而且满足二分性的,肯定要二分答案。先二分 1-k 这些人总共能走几步,再二分剩下一轮中 \(1-i\) 的人走一步。
考虑贪心。dfs 的过程中,我们先递归完子树,处理完子树内部的人的走步,然后考虑自己。那么显然,如果子树内走完之后剩下的树,能够向下走的步数不小于要求步数,那么直接向下走显然是优的,因为他不会影响到剩余人的走步(因为他自己就占了位,子树外的点不可能走到子树里来),而如果向下走不够,我们只能尝试向父亲走,于是过程中如果父亲被占了或者到根步数还不够,就不合法。
实现的话每个点可以打一个 tag,表示是否有人覆盖这个位置,如果是的话已经走了几步,这样就可以直接做两种二分了。
CF1795G
必然有解,于是考虑把每个点的度数减掉 \(a_i\) 之后做拓扑排序,并按拓扑序把边重定向,那么点对是好的当且仅当其不能在新的有向图上互达。于是问题就在于求可以相互到达的点对数量。
直接开 bitset 按拓扑序逆序更新可达点集合即可。但是空间开不下,于是考虑一次更新整张图所有点和 \([L,R]\) 中点的可达性,\(R-L+1\) 取一个适中的数即可。
也可以不用 bitset,改用 ull 压位,这时 \(R-L+1=64\),但 bitset 就是 ull 压位,二者实际上是一个东西。
复杂度 \(O(\frac{n^2}{64})\)。
CF1793E
显然优先使 \(a_i\) 小的人计入答案。于是先排序。
考虑设 \(f_i\) 表示前 \(i\) 个人全部计入答案,最多能分成多少组。
那么转移非常简单,\(f_i=\max_{0\le j\le i-a_i}(f_j)+1\)。注意 \(f\) 初值都是负无穷。
然后设 \(ans_i=f_i+n-i\),那么 \(ans_i\) 就是在前 \(i\) 个人合法的情况下所有人最多分组数。
于是维护单调栈,查询时在单调栈上二分即可,吗?
发现漏掉一种情况,就是把前 \(n-k+1\) 个人分成一组,得到 \(\min(n-k+1,\sum [a_i\le k])\) 的答案,这种情况在 dp 时并不会特殊考虑。
于是单调栈二分答案和这个取个最大值即可。
这场的 F 是众所周知的大原题。
CF1523F
考虑状压。显然可以设 \(f_{S,i}\) 表示当前激活的传送门集合是 \(S\),刚完成任务 \(i\) 时的最大完成数量,设 \(g_{S,i}\) 表示激活的传送门集合是 \(S\),完成了 \(i\) 个任务的最短用时。
那么显然先把任务按时间排序,然后从前往后枚举每个任务 dp。
转移有以下 4 种情况:
-
走到一个新的塔,直接 \(f_{S,i}\rightarrow g_{S',f_{S,i}}+dis\) 即可。\(dis\) 就是任务点和目前所有塔到新塔的最短耗时。
-
从塔去做任务,\(g_{S,i}+d(S,j)\le t_j\) 时,可以 \(f_{S,j}\leftarrow i+1\)。
-
从任务去做任务,那么 \(f_{S,i}+dis\le t_j\) 时,可以 \(f_{S,j}\leftarrow f_{S,i}+1\),其中 \(dis\) 仍然是任务点和塔的集合到任务点最短耗时。
-
用 \(f\) 更新 \(g\),直接 \(g_{S,f_{S,j}}\leftarrow t_j\) 。
发现 dp 复杂度是 \(O(m(m+n) 2^n)\),预处理 dis 等是 \(O((n+m)2^n)\)。
[CF360E]
题意:有向图,边点数都是 \(10^4\),有 \(k\le 100\) 条特殊的边,这些边的权值你可以任设成 \([l_i,r_i]\) 区间中的任意值,求能否使得 \(s1\) 到 \(t\) 的最短路小于 \(s2\) 到 \(t\) 的最短路,不能判断是否能相等。如果能小于或等于,给出一组解。
首先猜测最优解中边权只可能是 \(l_i,r_i\) 中的一个。这个证明比较显然。最短路中 \(A,B\) 都经过,显然设成什么无所谓。如果只有 \(B\),设成 \(r_i\) 显然赚,如果只有 \(A\),设成 \(l_i\) 也赚。
于是你考虑所有边初值都设成 \(r_i\),不断跑最短路,然后枚举每一条特殊边,判断是否有 \(dA(u) < dB(u)\),如果有,就把这条边设成 \(l_i\),否则不动。
在存在可行解的过程中,如果可行就直接构造出解了,否则寄了。
等于的情况类似,判断 \(dA(u)\le dB(u)\) 即可。
为什么正确?考虑你更新这条边,如果能更新 \(B\) 到终点的最短路,那么显然因为 \(dA(u) < dB(u)\),显然做完之后 \(A\) 的最短路就小于 \(B\) 了,直接输出答案即可。
否则只更新 \(A\) 到终点的最短路显然赚了。
而当 \(dB(u)\le dA(u)\) 时不更新的理由类似。
于是证毕。
CF1794E
打完div1来做div2感觉画风都不一样了。
五题场。感觉不如原神。
考虑哈希。
设两个大质数分别充当 base 和 mod,则显然
而我们只需要判断一个点的哈希值是否只比给定序列的哈希值大一个 \(base^x,0\le x < n\) 即可,这个可以通过 map 或者 pbds 库里的 hash_table 等解决。
而我们并不能实际上以每个点为根判断,于是考虑换根。这个是简单的,维护每个点子树以外到这个点的答案,搜索其儿子时,将这个答案加上该点子树的哈希值,减掉儿子子树的哈希值即可。
因为 \(n\le 2\times 10^5\),冲突率会比较大。可以用不同的 base 和 mod 做若干次(至少三次)这个事情,都合法才合法。
复杂度根据用 map 或者 hash_table \(O(n \log n)-O(n)\),都可以过。
CF1209H
妙妙结论题。
考虑把没有传送带的区域看成速度为 0 的传送带。
首先推一下柿子,能发现给一个固定传送带能量,能量效率是不变的,也就是我们找到一个较优的传送带可以能给多少给多少。
并且发现能量效率由传送带速度决斗,于是我们初始将所有空地的初速设为1,其余的初速设为 0(实际速度就是 \(s\))。
那么按照 \(s\) 从小到大考虑,每次看这个传送带能给多少能量,然后全部给的即可。
发现给完能量要满足之后都是非负的。
于是就是一个线段树后缀查询最小值,线段树后缀区间减。
线段树的节点上维护的是每条传送带刚进入时剩的能量,显然在不给这个传送带加能量时,进入时剩的能量是最少的。