Problem 2024.11

[Code+#4] 最短路

考虑一条 \(i \to j\) 的边,考虑 \(i \oplus j=1\) 的每一位 \(k_1,k_2\dots\),那么我们可以依次走到 \(i,i \oplus 2^{k_1},i \oplus 2^{k_1}\oplus 2^{k_2},\dots,j\),容易发现这样的花费与直接走 \(i \to j\) 相同,因此只需保留 \(\operatorname{popcount}(i \oplus j)=1\) 的边即可,注意考虑 \(0\),边数是 \(m+n\log n\) 级别,总时间复杂度是 \(\mathcal{O}(m\log n+n\log^2n)\)

这个做法又是利用了位运算各个位置独立的思想,而异或又有比另外两种更好的性质:从一个点出发,每改变一位就会加上这一位的边权,无论这位是 \(0\) 还是 \(1\)。利用这种性质考虑用较少的边组合出所有情况,换句话说,就是考虑边权在每一位上的变化,每次走点只改变一位边权,而 or 和 and 则要用一点贪心。

对于 or,从 \(s \to t\) 一定不比走其它的点差,因为 \(s,t\) 中的 \(1\) 无论如何都会被贡献,走其它的点相当于提供了更多增加答案的机会。

对于 and,首先有一个较小的做法是 \(s \to 1 \to t\),这样的花费是 \(s,t\) 中奇数的数量。为了让答案更小,我们可以让奇数向 \(2\) 连边,偶数向 \(1\) 连边,\(1,2\) 连边,答案显然是 \(0\)

P4219 [BJOI2014] 大融合

为什么没做出来:没有想到直接用 DS 维护每个点当前的 \(siz\)

询问需要知道某个方向的子树大小,因此把最终的树建出来,定根得到深度。然后考虑一组合并,把深度大的合到小的上,那么上面的所有点子树大小都要加上下面这棵树的大小。同时开一个 DSU 维护整棵树的根和联通快大小,就可以计算了。

CF718C.Sasha and Array

\(k\) 在斐波那契上没有直观的体现,考虑将其转为矩阵,那么就是乘上斐波那契数列转移矩阵的 \(k\) 次方。然后矩阵乘法有分配率,所以线段树维护矩阵和即可。为了防止边界讨论,可以将初始矩阵设为 \([f_i,f_{i+1}]\) 这样而不是 \([f_{i-1},f_i]\)

CF1665D.GCD Guess*

改天重写/oh。

\(\gcd(x+a,x+b)=\gcd(x+a,a-b)\)。考虑基础情况,\(a-b\) 为偶数,那么我们能知道 \(x+a\) 是不是偶数,如果令 \(a\) 是偶数,就能知道 \(x\) 是不是偶数,也就是 \(x\) 的二进制最低位。

\(30\)\(10^9\),考虑对二进制位逐位确定,因为这个东西显然没法二分。假设已经知道了 \(0 \sim i-1\) 位,前面的答案是 \(ans\),那么先给 \(a,b\) 减掉 \(ans\) 使得 \(x\) 的后面消成 \(0\),现在要看第 \(i\) 位是不是 \(1\),就是看它是不是 \(2^{i+1}\) 的倍数,因此只需满足 \(a-b=2^{i+1}\) 即可。

现在我们要依次确定第 \(0,1,2,\dots,29\) 位,那么 \(a-b=2,4,8,\dots,2^{30}\),同时还有 \(a,b\ge 0\) 的限制,也就是 \(a-ans \ge 0\),那么考虑 \(ans\) 是前 \(i-1\) 位的答案,不可能超过 \(2^{i}\)。观察前面,发现枚举的是 \(1 \sim 30\),确定的是 \(0\sim 29\) 总差一位,直接询问 \(a=2^{i-1}+2^i,b=2^{i-1}\),如果合法答案加上 \(2^{i-1}\) 即可。

Codeforces Round 845 (Div. 2) and ByteRace 2023

A.Everybody Likes Good Arrays!

两个奇偶性相同的数乘起来显然还是原来的奇偶性。因此原序列每个对 \(2\) 取模后连续段都要缩成一个数,模拟即可。

B.Emordnilap

你可以打出表来后大眼找规律,也可以直接推:对于一个排列 \(p\),假设它的逆序对数为 \(x\),显然 \(\operatorname{rev}(p)\) 的逆序对数就是 \(\frac{n(n-1)}{2}-x\),而跨越两个序列的贡献又是 \(\frac{n(n-1)}{2}\),所以总答案就是 \(n!n(n-1)\)

C.Quiz Master

考虑二分一个答案 \(mid\),然后枚举选取选手的最大值 \(x\),那么能力在 \([x-mid,x]\) 中的数都可以选,那一定选肯定是最好的,对于每个数,他的所有因数能够贡献,线段树维护 \([1,m]\) 中被覆盖的数的数量即可,这样是 \(\mathcal{O}(nd(V) \log m \log V+n\sqrt V)\) 的。

发现线段树是没有必要的,动态维护一个 \(cnt\) 数组即可;发现二分也是没有必要的,排序后双指针即可,注意一下细节就做到了 \(\mathcal{O}(n \sqrt V+nd(V))\)

D.Score of a Tree

对称性。

先让问题变得好看一点,第一秒 \(x\) 的权值是儿子的异或和,第二秒是孙子的异或和,\(\dots\)
考虑算每个点 \(x\) 被算了几次贡献,如果这个点的子树中所有的叶子都变成 \(0\),那它就没救了,这是取决于最深的叶子,因此记 \(f_x\) 表示 \(x\) 子树中距 \(x\) 最远的叶子距它有多远。

然后有一个非常神的想法:观察方案的对称性,如果某个方案在 \([0,f_x)\) 时间的贡献序列为 \(A\),那么把 \(x\) 各层的孩子选一个权值取反,那么 \(\neg A \to A\),方案是完全对称的,因此每个数贡献了 \(2^{n-1}f_x\),加起来即可。

E.Edge Reverse

要求最大边权最小,考虑二分答案,然后边权 \(\le mid\) 的可以反转或不反转,那就都连上。然后缩点成一个 DAG,问这张图是否有一个点可以到达所有点。结论如果只有一个点入度为 \(0\) 就是有,否则就是没有。证明:

  • 必要性:如果有两个点入度为 \(0\),那么第一个点到不了第二个点,其它点更是这两个点都到不了。

  • 充分性:若只有一个点入度为 \(0\),那么模拟整个拓扑排序的过程,容易发现这些点全都是由开始这个点扩展来的。

如果在缩点后的 DAG 上有一个点能到达所有点,那么原图就存在方案,因为在一个 SCC 可以走一条确定的路而不反复绕。

F.Comfortably Numb

长得一脸分治的样,考虑直接做序列分治,记 \(S\) 为前缀异或。假设现在在 \([L,R]\),中点在 \(mid\),那么只考虑跨过端点的贡献。枚举右端点 \(r\),对于每个 \(r\),我们要计算 \([L,r],[L+1,r],[L+2,r]\dots,[mid,r]\) 的贡献,考虑记一个 \(Mx\) 表示 $[mid+1,r] $ 的最大值,那么就存在一个 \(k\) 使得左端点 \(\in [L,k]\) 的最大值要另算,\(\in (k+1,r]\) 的最大值就是 \(Mx\),且 \(k\) 是逐渐向左扩展的。对前半段记一个后缀 \(\max\),记为 $suf_{i} $ 表示 \([i,mid]\)\(\max\)。那么分别处理。对于 \((k+1,mid]\) 这部分的 \(l\),权值是 \(Mx \oplus S_r \oplus S_{l-1}\),对于前面部分的 \(l\),权值为 \(suf_l \oplus S_{l-1} \oplus S_i\),那么两部分的贡献都是一个数对一个区间的数 $\oplus $ 的最值,可以用可持久化 trie 解决。

但是这样做麻烦了,考虑钦定最大值的位置在右边还是左边分别做一遍,这样另一个端点不断扩展,就是只有插入的异或最值,就不需要可持久化了。

第一种做法通常在对所有区间的最值求和是比较常见,因为第二种做法会算重,但是本题的 \(\max\) 是可重的。

[ABC217G] Groups

要敢于去直接做 dp 啊,同时理解清楚 dp 状态带来的限制。

显然是设 \(f_{i,j}\) 表示前 \(i\) 个人分成 \(j\) 组的方案数,初值是 \(f_{0,0}=1\),考虑转移:

  • \(i\) 新开一组,\(f_{i,j} \leftarrow f_{i-1,j-1}\)

  • \(i\) 和之前的数放在一起,但是不知道有几个组可以用,因此不会转移。但前面的已经是合法方案数了,假设 \(i\) 前有 \(x\) 个数与 \(i\)\(m\) 同余,那么它们都在不同的组里,因此 \(i\)\(j-x\) 个组可以放。

综上:\(f_{i,j}=f_{i-1,j-1}+f_{i-1,j} \times(j-x),x=\sum_{j=1}^{i-1} [j \equiv i(\bmod m)]\)

QOJ 6504.Flower's Land 2

前情提要:NOIP 模拟赛 Day 4.D

还是考虑给 \(0,1,2\) 各随一个矩阵,用矩阵乘法来维护能不能消空。区间加就是在 \(0,1,2\) 间轮换,简单维护一下即可,比上题简单的多。最后只需要 check 是否是单位矩阵即可。

CF165E.Compatible Numbers

\(a_i\operatorname{and}a_j=0\),则 \(a_i\)\(1\) 的位 \(a_j\) 必须为 \(0\)\(a_i\)\(0\) 的位对 \(a_j\) 没有限制。这样不太好说,但是可以把 \(a_i\) 看成一个集合,那么条件就是 \(a_j\subset(\neg a_i)\),那么可以记 \(f_i\) 表示集合 \(i\) 中有没有一个子集的值在 \(a\) 中出现过,转移可以枚举子集,但是是 \(3^{\log_2 V}\) 的,约为 \(3^{22}\),不行。考虑枚举子集中的一个数,看看删掉这一位有没有解,再看一位都不删有没有解,就行了。

CF1284D.New Year and Conference

区间有交并不比无交难处理很多。

这个问题就是说是否有一个 \(S\) 使得 \(S\)\(a,b\) 两侧的区间恰好有一侧不交。假设是在 \(a\) 侧有交,那么记 \(i\)\(a\) 侧的区间为 \([l_i,r_i]\)\(b\) 侧的为 \([L_i,R_i]\)。容易发现如果有 \(S\) 也就必然有一对 \(i,j\) 满足这个条件。

那么现在就是查询否存在下标 \((i,j)\) 使得 \(([l_i,r_i],[l_j,r_j]),([L_i,R_i],[L_j,R_j])\) 这两对区间恰有一对有交。

钦定有交的是 \(l,r\),把 \(l\) 从小到大排序,然后从小到大枚举,钦定 \(j<i\),发现此时有交仅需满足 \(l_i\ge r_j\) 即可,然后无交就更好维护了,只需要有一个左端点比当前的大或右端点比当前的小即可,贪心选最大的左端点,最小的右端点,后缀查询最值用 BIT 维护。

[蓝桥杯 2013 省 B] 连号区间数

Educational.

经典的结论是一个区间当且仅当 \(\max-\min=r-l\),做一个扫描线,枚举右端点 \(r\),那么就是 \(\max-\min+l=r\)。这样看上去还是不太能统计的样子,但是注意到 \(\max-\min \ge r-l\),因此 \(\max-\min+l \ge r\),因此等于 \(r\) 的部分就是最小值,且这样的区间一定存在 \(([r,r])\),因此只需要用线段树维护一个数组 \(f_l\),表示 \([l,r]\) 这一区间的 \(\max-\min+l\),右端点移动时实时更新 \(f\),同时维护 \(f\) 的最小值和最小值个数即可。

那么怎么更新呢?首先对于 \(f_l\) 来说 \(l\) 是不变的,可以作为初值。然后考虑维护这个 \(\max-\min\),由于方式类似仅考虑 \(\max\)。你每加上一个数 \(a_r\) 会影响到一些区间,而 \(f_l\) 的表现形式其实是后缀 \(\max\),一个后缀 \(\max\) 控制了一段区间。考虑使用单调栈维护这些区间,维护一个栈顶到栈底元素递增的栈,没加入一个 \(a_r\) 就把比 \(a_r\) 小的 pop 掉同时更新这段区间。更新要使用加法而非覆盖,因为你同时维护了 \(\max\)\(\min\)。然后更新完查询最小值即可。

CF526F.Pudding Monsters

注意到每行每列各有一个,那么把它缩成一个序列,第 \(x\) 个数表示的是第 \(x\) 行的数是在第几列,题目中的条件就又是值域连续段,和上一题完全一样的做法。

接下来更是高强度,见 代码源:数据结构

CF522D.Closest Equals

考虑记 \(pre_i\)\(a_i\) 上一次出现的位置,没有是 \(0\),那么就是求 \(\min_{l \le i \le r}[pre_i \ge l]i-pre_i\)

不可差分,但是 \(pre_i<i\),所以 \(pre_i \ge l \to i \ge l\),因此不需要管 \(i \ge l\) 的条件是 2-side。

直接枚举 \(r\),用 BIT 维护后缀 \(\min\) 即可,\(i\) 位置更新一个 \(i-pre_i\)

[ABC346G] Alone

枚举每个数算贡献,假设 \(i\) 这个位置的值上一次出现的位置是 \(pre_i\),下一次出现的位置为 \(nxt_i\),那么合法的区间就要满足 \(pre_i<l \le i,i \le r < suf_i\)。对于每对 \((l,r)\),只要存在一个 \(i\) 满足这个条件它就是合法的,但是直接算会重复算一个点对多次。考虑以 \(l\) 为横轴,\(r\) 为纵轴建系,那么问题等价于矩形面积并,扫描线即可。注意这里的每个点都是有实际意义的。

【美团杯2021】A. 数据结构

一个数有贡献的情况很多,但是没有贡献的要求就比较严格了,所以考虑一个数 \(x\) 没有贡献的条件。

  • \(x\) 的所有出现都在 \([l,r]\) 中。
  • \(x-1\) 的所有出现都不在 \([l,r]\) 中。

如果这两个条件都满足了,\(x\) 也就没有贡献了。假设 \(x\) 最左和最右的出现位置分别为 \(L,R\),那么 \(l,r\) 需要满足 \(l \le L,r \ge R\)。因为 \(x-1\) 不在 \([l,r]\) 中,所以 \([l,r]\) 肯定在 \(x-1\) 相邻两次出现的中间,假设为 \(p_i,p_{i+1}\),那么 \(l,r\) 需要满足 \(l>p_i,r<p_{i+1}\),综合一下就是枚举 \(x-1\) 所有相邻的出现位置,\(l,r\) 如果满足 \(p_i+1 \le l \le L,R \le r \le p_{i+1}-1\) 那么 \(x\) 就对 \((l,r)\) 没有贡献。

这样的限制是矩形的形式,以 \(l\) 为横轴,\(r\) 为纵轴把这些矩形建出来(拆成入边和出边)后扫描线,需要支持区间加单点查。答案就是 \(n+1\) 减去没有贡献的数。

如果 \(x\) 不存在相当于没有第一条限制,\(x-1\) 不存在相当没有第二条限制,\(x\) 需要枚举到 \(n+1\)。注意 \(x-1\) 不出现的位置还有左右两端。

「GLR-R3」惊蛰

首先有结论 \(b\) 中的数一定在 \(a\) 中出现过。证明我不太会,引用 LuoTianyi_Official 题解中的话就是:

每个数如果要往下修改,改成和上一个数相同一定不劣(因为再往下对后面的限制更多,但没有任何好处),而如果要往上修改,改成和后面某个数相同一定不劣(因为越往上代价越大,而如果改成了后面两个数之间的一个数,显然不会比往下调整到后面出现过的第一个数优)

那么可以先给原序列离散化。

dp,设 \(f_{i,j}\) 表示前 \(i\) 个数,第 \(i\) 个数是 \(j\) 的最小花费(这里是指离散化后的)。

那么 \(f_{i,j}=\min_{k\ge j}f_{i-1,k}+cost(b_j,a_i)\)\(b\) 是从小到大排序去重后的 \(a\)

考虑 \(i-1\)\(i\) 的过程,是对 \(f_{i-1}\) 做了后缀 \(\min\) 后加上 \(cost(b_j,a_i)\)

\(cost(b_j,a_i)\) 可以拆一下,若 \(b_j<a_i\) 则都是 \(C\)\(b_j \ge a_i\) 则是 \(b_j-a_i\)。两部分都单调不减,而后缀 \(\min\) 也单调不减,现在就有两端单调性。

现在考虑后缀 \(\min\) 怎么做,因为单调性,\(b_j<a_i\) 这一段的数无需改变,只有左边一段靠右的某些数会变成右边一段的最小值。所以我们要找一段单调区间内第一个 \(> x\) 的数的位置。具体地说,我们要在 \([1,a_i)\) 中找第一个 \(>v\) 的数的位置 \(p\),并将 \([p,a_i)\) 全部改成 \(v\)

需要维护:区间覆盖,区间加法,区间每个点加一个固定值,维护区间最大值。最后的答案就是第一个数。

我的代码过了后三个子任务没过前两个,可能是 \(0\) 的处理有问题,太菜了。

CF1523G.Try Booking

Key Observation:最终的答案和是 \(\sum_{i=1}^n \frac{n}{i}=n\log n\) 级别的,因此每次只要找到一个区间复杂度就是可以接受的。

按照区间长度从大到小插入区间,每次插入完长度为 \(x\) 的区间后(此时我们考虑了 \(\ge x\) 的区间)计算答案。设计函数 \(calc(l,r)\) 表示 \([l,r]\) 时间的答案。注意我们要依次处理询问,所以找到合法区间中时间最早的一个 \([l',r']\),然后划分成 \([l,l')\)\((r',r]\) 两个子问题求解即可。这样每个区间都会增加 \(1\) 的时间复杂度,所以是正确的的。

至于怎么找第一个合法区间,由于是动态的二维数点,直接树套树维护编号最小值即可,时间复杂度三个 \(\log\)

CF793F.Julia the snail

考虑扫描线右端点,用数据结构维护数组 \(f_i\) 表示左端点为 \(i\) 的答案。

那么右端点更新时,增加了一些可用的区间,对于一个区间 \([l,r]\),它能做的修改是:将所有 \(1 \le i \le l,f_i \ge l\)\(f_i\) 改为 \(r\),原因就是能够走到 \(l\) 或上面的都可以滑下来然后走这个区间。当然滑下来不能出边界,因此 \(i\) 必须不超过 \(l\)

现在用数据结构维护这个操作,形式是类似 Segment Tree Beats,我们也考虑维护最大值和严格次大值,如果 \(l<\) 严格次大值就暴力递归。这样因为每次暴力递归也至少合并了两个数,复杂度分析和 Beats 是一样的,也是一个 \(\log\)。注意这种的标记是打在最大值上的,只对最大值操作,因此 pushdown 的时候要判一下是否是最大值。

posted @   aCssen  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示