20210820K 复盘
看题。发现 t1 是个路径计数。完蛋了不会数数。看半天,没啥想法
去看 t2,emmm...
看 t4,如果每个点维护其所在的前缀和,需要支持区间加等差数列区间求 max,不大容易办到啊,最后写了暴力
看 t3,emmm..难道要写可持久化 01trie 然后超级钢琴式维护?哦哦,好像确实可行啊,01trie 上要每个点维护它头上一条边的经过次数,两棵树的一条边对应次数相减就能知道区间数字插出来的 01trie 了
复杂度 \({\cal O}(n\log n +k\log n)\),就是码量有点大(也不是很大)
遂回来看 t2,还是没啥想法,乱写了一个 dp 就跑了
然后把 t3 写了,测样例过了,希望没锅
然后考后发现:t3 竟然只有 40pts?然后交流发现 m 的范围最大是 n^2 的!而不是 n 的!复杂度算错了
然后 t2 和 t4 都被暴力干过去了,惊讶
s2oj508. A.[2021省队集训]路径计数(count)
这个环很简单,我们考虑任何一种合法的方案都相当于是在某条无向边上来回走很多遍然后回到点 1 的过程
于是可以将无向图定向,比如 \(1-2\) 边需要经过 \(v_1\) 次,那么我们可以将 \(1-2\) 的无向边拆成 \(x_1\) 条 \(1\rightarrow 2\) 的有向边以及 \(v_1-x_1\) 条 \(2\rightarrow 1\) 的有向边,定向完成之后,一条欧拉回路就是一个合法的路径
进一步的,我们发现一旦第一条边的 \(x\) 被确定,那么为了保证存在欧拉回路,所有边的定向条数也会被确定下来!
那么只要枚举 \(x_1\),然后把图计算出来,利用 BEST 定理求欧拉回路数量即可
s2oj562. B.BRT
原题:CF187D BRT Contract
发现所有的路灯都是相同周期的同步变换的。所以如果你走着走着被卡了一下,那么就会卡到直到一个整周期开始的时刻,相当于你的时间被和路灯 "对齐" 了。
【以下基本翻译自 cf 原题题解】
我们按照如下意义定义 \(t[i]\):假设一辆车恰好在第 \(i\) 个路灯变绿的时候从 \(i\) 出发,他走到终点所花费的时间为 \(t[i]\)
另外,我们将绿灯区间开始的哪些时刻记作 \(t0\)(也就是,每过 \(g+r\) 秒 \(t0\) 就会出现一次)
如果一辆车到达 \(i\) 的时候是红灯,那他就得等到下一个 \(t0\) 时刻时才能往前走。所以考虑到所有的灯都是同步变换的(并且红绿的时间段长也是固定的),如果我们知道了所有的 \(t[i]\) 就能方便的求出答案
显然 \(t[n] = \text{第 n+1 段长度}\)。为了计算出 \(t[i]\) 来,我们需要找到那个从 \(i\) 出发,遇到的第一个被卡的 \(j\) 来,这样就有 \(t[i] = t[j]+d(i,\ j)+w\),其中 \(w\) 是你卡住需要等的时间
于是问题就落在了如何快速找到 \(j\) 上。我们倒序枚举 \(i\),假设 \(p = g+r\),\(ta = d(i,\ j)\bmod p\),那么一个 \(j\) 合法显然有 \(ta\in [g,\ g+r)\)
于是考虑线段树。设路程的后缀和 \(s[i]\) 表示从 \(i\) 到终点的距离,拆分 \(d(i,\ j) = s[i] - s[j]\)。考虑到
这样,每一个 \(i\) 形成一个查询区间,我们查询这个区间中最小的那个 \(j\);将 \(i\) 插入的时候,我们将 \(-s[i]\bmod p\) 位置单点修改成 \(i\) 即可
这样,我们可以求得所有的 \(t[i]\)。对于每个询问的回答也可以用完全相同的方式。总共复杂度是 \({\cal O}((n+q)\log n)\)
s2oj563. C.xor
原题:CF241B Friends / 异或粽子加强版
【题解】异或粽子&加强版 - RingweEH - 博客园 (cnblogs.com)
题解 P5283 【十二省联考2019异或粽子】 - hsfzLZH1 的博客 - 洛谷博客 (luogu.com.cn)
[十二省联考2019] 异或粽子 - Apocrypha - 博客园 (cnblogs.com)
首先消除 \(i<j\) 的影响,求所有 \(i\ne j\) 的第 \(k\) 大,把 \(k\) 乘二就行了
处理前 k 大的方法中,除了暴力排序,超级钢琴式取值,还可以有这样一种思路:
先求出第 \(k\) 大的值,然后找所有大于等于这个值的所有数对的和
那么接下来的算法分为了两步:
求第 \(k\) 大:
二分第 \(k\) 大,假设二分出来了 \(mid\),我们显然要检查异或值大于等于 \(mid\) 的数对有多少对
不妨枚举一个点 \(i\),计算所有和 \(a[i]\) 异或值大于等于 \(mid\) 的数的个数,这个过程可以在一棵高位到低位插好的 01trie 上二分得出,具体来说很像数位 dp,设函数 queryrank(num, lim, x, dep) 表示我要在 01trie 上查询和数字 num 异或起来 >= lim 的数有多少个
-
如果我们发现 lim 的 dep 位为 1,那么我们这一位必须异或出来一个 1,才有可能值 >= lim,所以直接走下去,
return queryrank(num, lim, ch[x][c ^ 1], dep - 1)
-
否则,我们往 "这一位异或起来是 1" 的那边子树看一看,位于这个子树内的任意一个数和 num 异或起来肯定都是要 > lim 的,所以 += 那边子树的 siz,然后往 0 走一步:
return siz[ch[x][c ^ 1]] + queryrank(num, lim, ch[x][c], dep - 1)
如此实现,即可求得第 \(k\) 大的值
求大于等于 \(k\) 的数对的异或的和
类比刚才在 01trie 上二分的过程,我们现在枚举每一个 \(i\) 计算,和 \(a[i]\) 异或起来结果 >= C 的数对和他的异或值之和。和上面一样的过程,唯一不同的是,如果 lim 的这一位为 0,我们往 "这一位异或起来为 1" 的子树看一看,要 += 所有在那边的子树中的数和 num 的异或值的和。
这个怎么处理?不妨分二进制下每一位单独考虑,如果我们可以得知那边子树所有数在每一位上的 0 的个数,以及 1 的个数,我们就能再配合 num 的每一位求出 num 和那边子树中每一个数的异或值的和
那么如何得知每一位上 0 的个数和 1 的个数?有一个小技巧是:将 a 排序后再依次插入 trie 里,那么 01trie 上经过任意节点的所有数一定是排序后 a 上的一段连续区间。这样,我们在 a 上对每一位做 01 前缀和即可!
至此,本题被圆满解决,第一部分和第二部分复杂度均为 \({\cal O}(n\log ^2\text{值域})\) 的
思维链很长,可以多捋几遍
s2oj564. D.核糖
原题:Luogu P4192 旅行规划
形式化题意:
维护序列,支持区间加等差数列,区间求最大值
大分块题,我实在是不善分块啊....
非常巧妙,考虑分块处理,在进行一些修改操作之后,对于某一个位置 \(i\) 来说,设其所在的块 \(\mathtt{B} = pos[i]\)
此时的 \(i\) 位置上的真实值一定是可以写成:
即,很多条等差数列重叠相加得到的结果相当于是一个等差数列,具有首项 \(a_0\),而后的每一项都 += 公差
那么考虑改写这个等式:
哦,由于 \(s[i]\) 不变,我们可以将其看作是二维平面上的一个点 \((i,\ s[i])\),每一次我们要求块 \(\tt B\) 的最大值的时候,相当于是拿着一条斜率为 \(-d[{\tt B}]\) 的直线去切,问切到的最大截距是多少
我们应该对每个块维护上凸壳。和线段树这样的数据结构不同的是,分块不会实时的去维护好 "最大值是哪个",而是去维护好 "能支持查询到最大值的信息"。每次查询一个块的最大值的时候,我们在该块的凸壳上二分;每次修改的时候,我们对于整块的直接修改其首项,公差的标记;对于边角块加到 s[] 值上,然后暴力重建凸壳。
复杂度 \({\cal O}(q\sqrt{n}\log n)\)