集训队互测2024
懒,随缘更。
Round 1
优惠购物
看完题会有几个结论:
- 如果一次性使用了 \(c\) 个金币,那么在更前面使用更优。
- 对于每个数最上面的 \(A_x\bmod c\) 个金币,优惠券越前面用越好。
我们来考虑一下优惠券的使用,优惠券越前面用越好吗?不一定,这样优惠券在后面产生,可能用不到。优惠券越后面用越好吗?也不一定,有可能优惠券实在太多了,需要都用优惠券。
这里产生了一个矛盾,但是是有办法解决的。我们从前往后考虑每个物品,并优先使用优惠券。接着我们可以反悔。假设我们对 \(i\) 使用了 \(C_i\) 个优惠券,那么最上面 \(A_i\bmod c\) 个优惠券是不会被用金币反悔的,因为这里是一个金币换一个优惠券,肯定不优。中间有若干段整段的 \(c\) 个优惠券,如果用 \(c\) 个金币反悔,会得到 \(c+1\) 个优惠券。最后有一个非整段的优惠券,同样的,如果用金币去完全兑换,可以多得到一张优惠券,否则只能得到金币对应数量的优惠券。
从这里可以看出,对于一个物品,如果有可以反悔的一段优惠券,肯定优先使用金币去反悔这段优惠券,而不是直接用金币去购买这段商品。这样大体上已经完成了,最后需要考虑需要的优惠券不足以兑换一整段之前的优惠券的情况,这时需要考虑到底是将前面的一段优惠券兑换一部分,还是将金币留给自己,这个可以根据留给自己会/不会多一张优惠券,以及两者的大小关系来判断。
用堆维护,每次取最小的,时间复杂度 \(O(n\log n)\)。
网格图最大流计数
好像有点超前了/qd,这里就写一写部分分。
\(s_{i,j}=0\) 的点可以看作这个点不能通过,流可以看作不相交路径,也就是说,我们需要求一张有障碍的网格图上,不相交路径的最大值以及计数。
对于 \(n=m\) 且最大流为 \(n\) 的情况下,可以直接上 LGV 引理,先 \(O(nk^2)\) dp 求出每对起终点之间的路径条数,然后直接求矩阵行列式的值即可。
对于 \(n\leq 10\) 的,有两种做法,一种是枚举起终点集合,然后求行列式。另一种是考虑每个起点,状压有哪些终点选取过了,然后选取一个终点,计算排列逆序数的贡献,复杂度 \(O(2^kkn)\)。
然后要用什么 Pfaffian ,不会了。/hsh
Round 2
序列
1s 跑不过去 7e8 次乘法取模什么成分?long long 改 int 快一倍什么成分?
枚举值域 \(m\leq n\),大的显然可以插值。倒着dp,如果形成了 \(a_x>a_y,x<y\) 的情况,那么将 \((a_x,a_y)\) 之间的数视作删掉。则可以设出 dp 状态:\(dp_{i,j,k}\) 表示从后往前到了第 \(i\) 个,还剩 \(j\) 个数,目前最小值为 \(h\) 的方案数。转移要么更新最小值,要么和最小值组成 \(20\) 形状,就删掉了中间的数。直接 dp 是 \(O(n^5)\) 的,进行一些简单的优化就可以变成 \(O(n^4)\),加上插值总共可以做到 \(O(n^4+nq)\)。
没有创意的题目名称
看到这个题一脸懵逼,于是先看个特殊性质,发现 \(lim_i=i\) 的答案是 \(\frac{n(n+1)}{2}+1\),那个 \(+1\) 是 \(f_i=i\) 的。其余答案的序列应该长成:存在一个 \(k\) ,满足 \(i\leq k\) 的有 \(f_i=i\),\(f_{k+1}\) 在前面 \(i\) 个随便选一个指向过去,之后 \(f_{k+2}\) 以后的可以唯一确定且一定有解,构成了一个循环。
然后写个暴力会发现不仅是这种情况,几乎所有情况下都有循环节。
先证明一下存在循环节。如果存在 \(f_i=f_j\),则 \(f_{i+1}=f_{f_i+f_1}=f_{f_j+f_1}=f_{j+1}\),也就是说一旦出现两个相同的数,就会形成循环节。
这里分 \(f_i=0\) 和 \(f_i\not=0\) 讨论。先看 \(lim_i=0\),枚举前面 \(f_i=i\) 的到哪里,假设到 \(i\),那么 \(f_{i+1}\) 会不指向自己。如果 \(f_{i+1}\) 指向 \([0,i]\),那么可以像上面直接判断,否则 \(f_{i+1}\) 指向更后面的数。因为 \(lim_0=0\),所以 \(f_{f_i}=f_i\),也即如果 \(f_{i+1}\) 确定后一定会形成循环节。
但是当 \(i+1\) 指向后面时,形成合法序列还要满足一些其它条件。首先是,\(2(i+1)\leq n\),因为若 \(2(i+1)>n\),那么 \(f_{i+1}\) 与 \(f_{n-(i+1)}\) 加起来就会大于 \(n\),不合法。然后可以推出 \(2f_{i+1}\leq n\)。可以发现,这对于循环节内所有指向后面的数都是成立的。其次,如果循环节内某个位置 \(f_i\) 指向了 \(i-c\),则根据定义,\(f_{i+1}=f_{i-c+1}\)。也就是说,如果某个位置指向前面了,那么接下来的位置都要指向前面。
可以证明,有上面两个性质,就是充分的,于是就可以 \(O(n^3)\) 计数了,进行一些剪枝就可以通过。或者可以维护区间乘积做到 \(O(n^2)\)。
然后考虑 \(f_0\not=0\)。首先,有 \(f_0=f_{f_0}=f_{2f_0}=\dots\),又构成了循环节,也就是说,循环节是从 \(0\) 开始的,所以只需要枚举循环节长度 \(c\),然后对于循环节每个位置的取值,要么是指向和自己相同的数,要么均指向加上一半循环节长度的数。直接和上面类似计算就是 \(O(n^2)\) 的。
Round 3
Permutation Counting 2
首先我们钦定两维分别有 \(x,y\) 个 <
,则最后的答案可以两维分别来一次二项式反演得出。
有 \(x,y\) 个 <
等价于有 \(n-x,n-y\) 个连续段,所以可以转化成钦定有 \(x,y\) 个连续段。
考虑排列与其逆排列连续段之间的关系,可以发现,当我们在放原排列的一个连续段的时候,其会在逆排列的若干个连续段最后加上若干个数。具体的,设 \(a_{i,j}\) 表示在放原排列第 \(i\) 个连续段的时候在逆排列第 \(j\) 个连续段最后放了 \(a_{i,j}\) 个数,则满足如下条件的 \(a\) 唯一对应一个排列:
- \(\sum a_{i,j}=n\)。
- \(a\) 每行每列都有值。
前一个限制可以插板法做,后一个限制可以钦定某几行某几列为空,然后容斥,同样可以拆开成两维分别来一遍,时间复杂度 \(O(n^3)\)。
化学实验
感觉,LCT,2log,1s-,有点神秘!
首先这种题看看就是维护 Kruskal 重构树。先假设所有点父亲都是 \(n+1\)。于是你考虑,加入一条边对于现在的 Kruskal 重构树有什么影响。
设加入 \((x,y)\),在现在的树上 LCA 为 \(z\),如果 \(x=z\) 或者 \(y=z\),则不需要操作。否则通过模拟 Kruskal 的过程可以看出,就是按照点的编号顺序合并 \((x,z),(y,z)\) 两条链。
这个怎么合并?直接启发式合并感觉复杂度不是很对。题解给了一种合并方法:将合并后原来属于同一条链的称为连续段,则显然可以通过在 splay 上二分之类的做到 \(O(C\log C)\) 合并,其中 \(C\) 表示连续段个数。下面证明 \(C\) 的总和为 \(O(n\log n)\) 的。
记势能函数 \(w_i=\log(fa_i-i)\),初始时 \(\sum w_i=O(n\log n)\),只需要证明每次操作至少减少 \(O(C)-O(\log n)\) 势能即可。
假设两条链上连续段分别为 \([x_1,y_1],[x_2,y_2],\dots,[x_C,y_C],[l_1,r_1],\dots ,[l_C,r_C]\)。
原势能是 \(\sum\limits \log(x_{i+1}-y_i)+\log (l_{i+1}-r_i)\),现在的势能为 \(\sum \log(l_i-y_i)+\log(x_{i+1}-r_i)\)。
根据基本不等式 \(x+y\geq 2\sqrt {xy}\),有 \(\log (x+y)\geq \frac{\log x+\log y}{2}+1\)。所以左边 \(\log(x_{i+1}-y_i)\geq \frac{\log (x_{i+1}-r_i)+\log (r_i-y_i)}{2}+1>\frac{\log (x_{i+1}-r_i)+\log (l_i-y_i)}{2}+1\),左边减去右边就为 \(O(C)-O(\log n)\)。
总复杂度 \(O(n\log ^2n)\),严重怀疑 \(\sum C\) 是 \(O(n)\) 的。
Round 4
数据库
链接先鸽了,等 QOJ 有题再说。
建图方式不同被搏杀了!因为没注意到 \(m\) 很小!
我们将一个流量看作一个仍在序列中的数,一个数在其对应的位置产生,可以跳到下一个最近的数,也可以转化为另一个数。
将一个点拆成三个点,记作 \(i_0,i_1,i_2\),其中 \(i_0\) 可以看作原序列上的点,\(i_1,i_2\) 可以看成流量在数之间跳跃的点,\(i_1,i_2\) 之间有一条边限流。
对于每个 \(i\),按照如下方式建图:
- \(((i-1)_0,i_0,0,m)\)
- \(((i-1)_0,i_1,c_{a_i},1)\)
- \((i_1,i_2,-\infty,1)\)
- \((i_2,i_0,0,1)\)
- 令 \(j\) 为最近的和 \(i\) 相同的点,则连边 \((j_2,i_1,0,1)\)。
这样总流量是 \(O(m)\) 的,直接跑费用流是 \(O(mnq)\) 的,可以喜提 \(40\),注意到原图是个拓扑,则可以先跑出最短路,然后用 dijkstra 跑费用流即可。
左蓝右红
链接先鸽了,等 QOJ 有题再说。
为啥没想到扫描线呢?
仿照样例解释,我们将图分成蓝,红,白三块,有如下结论:
- 只要每个矩形其所有边相邻不同色,则区域封闭
- 两个相邻的封闭区域一定是一个白色一个蓝色。
- 不同色封闭区域相互不交。
其中第三条是因为题目中不允许某个区域被红色和蓝色都覆盖了奇数次。因为矩形横纵坐标互不相同,因此如果红色区域和蓝色区域相交,则相交区域一定有恰好被覆盖了 \(1\) 次的区域,矛盾。
这时可以扫描线,从左到右考虑每条竖线,并维护横线构成的集合。横线之间形成配对关系,即考虑从小到大的第 \(2i-1\) 和 \(2i\) 条直线,其中间是有色的,并且这两条横线是同色的,而 \(2i\) 与 \(2i+1\) 条横线之间是没有颜色的。在加入两条横线之后,会·导致其之间的横线匹配关系变化,需要将重新配对的横线合并起来,用一个并查集即可实现。这样是 \(O(n^2)\) 的。
进一步的,我们发现,暴力合并并维护连续段只会操作 \(O(n)\) 次,因此可以用 ODT 维护,复杂度 \(O(n\log n)\),代码摆了,只有 \(O(n^2)\) 的代码。
Round 5
Xor Master
链接先鸽,等 QOJ 有题再说。
既然大家都喷过了,我就不多说什么了(
显然对 \(S_0\) 维护一个线性基,每个数可以在线性基中消成最大的,并且将 \(a\) 前缀异或起来。
考虑动态维护这个过程,每次往线性基中加入一个向量的时候,把这个向量和之前的向量消成位独立的形式,这样 \(a\) 中每个元素的变化就可以 \(O(1)\) 算出来,单点异或要把异或的值消成最小然后再异或进去。因为只会加入 \(O(\log V)\) 个向量,对原序列维护线段树,时间复杂度就是 \(O(n\log ^2V+q\log n\log V)\),好像不太能过。
这种区间位运算区间查询和的题我们考虑采用诡异操作那题的思路,也即在线段树上翻转下标和存储的值,这样只需要 \(f(n)=2f(n/2)+O(\log n),f(n)=O(n)\) 就可以重构整棵树,并且修改只需要对 \(O(\log n)\) 个数维护加法就行,时间复杂度 \(O(n\log V+q\log ^2n)\),完全能过!
submission 先鸽了。
栞
首先考虑 \(p_i=i\) 的情况,你会发现,只需要值域极小连续段的个数 \(\geq k\),就可以重排成 \(p_i=i\)。
现在来考虑不有序的情况,枚举一段区间 \([l,r]\),表示钦定这一段是原串变成 \(p\) 的一个极小划分,需要满足:
- \(p[l,r]\) 有序。
- 除了最后一个数,其余的数都要大于前面的点,如果最后一个点小于前面的点,那么最后一些段都是单点。
容易结合划分过程发现上面是充要条件,直接 dp 就可以做到 \(O(n^3)\)。
submission 鸽。
数据结构
首先 \(k=0\) 是平凡的树剖,不需要任何改动。
然后考虑 \(k=1\),我们考虑修改一下树剖,使之可以维护这个过程。在树剖的时候,对于一条重链,先将重链上的点标号,然后对所有重链上的点的儿子标号,如果一个点已经被标号了就不标号。这样子每条重链,以及每条重链的轻儿子的编号都是几乎连续的,这里说几乎连续的原因是第一个点可能在之前就被标号过了,所以可能不连续。不过没关系,特殊处理就行!
这样子的话这些操作就都可以做了!考虑扩展到 \(k\geq 3\),你会发现,只需要将上面树剖的时候,处理每条重链时,再标记子树内第二层,再标记子树内第三层就行,这样对于某个点子树内的某一层(包括重儿子),其在树剖序上是 \(O(k)\) 段的,暴力修改就行,同时一个点的子树内编号也是大体上连续的。总复杂度 \(O(nk^2\log ^2n)\)。
submission 鸽。
Round 6
Grievous Lady
尼玛的怎么放在 QOJ 上过不去了。
首先我们有一个 dp,设 \(f_{i,j}\) 表示选到第 \(i\) 个点,\(a\) 的总和为 \(j\) 的最大 \(b\) 总和,这样直接做是 \(O(n^2V)\) 的。
我们发现,如果一个状态把另一个二维偏序掉了,那么小的那个状态是没有用的,如果你这样筛状态,打个表可以发现大概只有 \(O(n^2)\) 个状态,转移归并可以做到 \(O(n^3)\)。
题解声称仔细分析可以被扔掉的点可以做到只剩下 \(O(n)\) 个状态,但是我没有想到。但是我发现,如果一对数 \((a,b)\) 差的比较大,那么这对数选 \(a\) 还是选 \(b\) 几乎是确定的,所以可以定一个阈值,认为如果 \(|a-b|\geq lim\),那么直接选掉,否则再用上面那个 dp。如果取 \(lim=c\times \frac{B}{\sqrt n}\),其中 \(c\) 取 \([2,3]\) 中的数,那么最后剩下的约有 \(\sqrt n\) 个数,然后用上面那个 dp 做的复杂度是 \(O(n\sqrt n)\) 的。
看上去这个复杂度已经稳过了,但是因为拿去 dp 的数已经比较不随机了,所以状态数会带上一个比较大的常数,大概有个 \(10\) 倍,就会跑个三四秒。但是,如果我们用 meet in middle 的思想,分成两边做,然后中间决策单调性合并,可以做到比较小的常数。
这样再 tomato OJ 上可以稳过,但是 QOJ T 了,而且最后一个包要么 1.3s 要么 TLE,有无懂哥教教。
Axium Crisis
首先我们考虑 \(o=3\) 的情况,树的边权是确定的。我们考虑一个暴力 dp,暴力把每条路径拉出来,按照字典序排序,则 01Trie 的大小就是串长总和减去相邻字符串的 LCP 长度。我们设 \(dp_{i,S,j}\) 表示决定到第 \(i\) 条路径,已经选的边集为 \(S\),上一个选的路径为第 \(j\) 条,的最大 01Trie 大小,暴力转移可以做到 \(O(n^42^n)\)。优化可以考虑让 \(j\) 表示上一个选的和当前的 LCP 大小,每次转移的时候要把 \(j\) 对第 \(i\) 个和第 \(i+1\) 个的 LCP 取 min,这样可以做到 \(O(n^32^n)\)。
然后考虑边权不定的情况,如果我们暴力把所有路径拉出来,然后爆搜所有可能的路径,这样会有 \(O(2^nn^2)\) 条路径,然后直接状压 dp,复杂度是 \(O(4^n\operatorname{poly}(n))\) 的,不太能过。不过发现对于一条路径,如果其边集为 \(T\),则转移的时候只需要关注 \(T\cap S=\empty\) 的 \(S\) 即可,这样的 \(S\) 只有 \(2^{n-|T|}\) 个,而这条路径可能搜出 \(2^{|T|}\) 种状态,所以对于某一条路径的转移总和是 \(O(2^n)\),那么总共就是 \(O(n^22^n)\) 的。
但是复杂度并没有得到根本改观,因为我们有一个和当前 LCP 取 \(\min\) 的过程。但是可以发现,一条路径最多总共只能产生 \(O(2^n)\) 个状态,而每个状态最多被取 \(\min\) \(O(n)\) 次,所以如果我们只对存在的状态取 \(\min\),那么复杂度是 \(O(n^32^n)\) 的,可以接受,这样整个题就做到了 \(O(n^32^n)\),输出方案需要写个栈来回退,不想写了(
落日珊瑚
这是碰都不能碰的花题。
Round 7
不是这一道据数构结题
首先我们考虑一下怎么 \(O(n)\) 回答每个询问。先考虑排列的情况,假设排列为 \([1,n]\),则枚举每个 \(x\),将 \(\geq x\) 的设为 \(1\),\(<\) \(x\) 的设为 \(0\),则可以发现,如果前 \(n-x+1\) 个存在 \(0\),那么会一直存在,直到最后一个 \(1\) 换进来。所以不用操作的次数就是 \([1,x]\) 最小值为 \([n-x+1,n]\) 排列的个数。
再考虑非排列的情况,发现判定条件是几乎不变的,也就是说我们枚举一个 \(x\) ,则前 \(x\) 的最小值应该和排好序之后前 \(x\) 个的最小值相等,容易发现这应该是充要条件。
我们现在已经有了一个 \(O(n)\) 单次询问的做法,现在来考虑一下优化。考虑从右到左扫描线,假设扫到 \(l\),维护对于每个 \(i\),\([l,i]\) 合法的右端点。如果我们用单调栈维护出每个以 \(l\) 为左端点,右端点在 \([l,n]\) 的最小值,那么在一个最小值的段里,可行的右端点结束位置分别是最小值位置往右 \(1\sim siz\) 个大于这个最小值的位置,其中 \(siz\) 是这个最小值在前缀 \(\min\) 中出现的次数。发现每次操作后,改变的位置是 \(O(1)\) 的,用树状数组暴力维护即可。找到某个位置往后的一个位置可以用倍增实现。
顺带一提,如果找到往后的一个位置如果暴力可以跑得比暴力还快。
意念力
前半部分还是比较好的,后半部分不就是 dirty work 吗(
这个集合是实在不好算,我们计算 \(f_i\) 表示用 \(i\) 种颜色去染色的方案数,最后二项式反演出 \(g\) 即可。
但是 \(f\) 仍然是难以计算的。我们考虑一条链,从左到右考虑。发现实际上每个点只需要考虑和左边的不一样,而且左边的要和这个点不一样的点都是不一样的,所以这个点的颜色数可以简单计算出来。对于边权为 \(1\) 的情况,可以简单计算这个权值。对于边权不为 \(1\) 的情况,答案可以表示为 \(\prod\limits_{i=1}^{n}{(A_i-x)}\) 形式,这样的形式可以用分治 NTT 求出这个多项式然后多点求值做到 \(O(n\log ^2n)\),也可以先离散对数求出 \([1,n]\) 每个数的离散对数,然后用任意模数 NTT 对其卷积,最后离散对数回去即可。
树上并没有一个明确的和链类似的做法,但是我们可以考虑构造一个。随便定个根,对于每个点,按照其与根的距离排序,依次加入,每个点只考虑和已经加入的点之间的限制。容易发现,每个点关联的其它点是都是不同的,因为当前点到 \(LCA\) 的距离显然要 $\geq $ 关联点到 \(LCA\) 的距离,这样考虑 \(x\to y,x\to z\) 的某个公共点,\(z\) 在这个公共点改道 \(y\) 一定比走到 \(y\) 更近,这样的话如果 \(x\to y,x\to z\) 的距离 \(\leq k\),\(y\to z\) 的距离一定 \(\leq z\),因此只需要点分求出每个点的关联点个数,然后就可以和上面一样做了。
因为点分,分治 NTT和多点求值是 dirty work,所以只有一个 \(O(n^2)\) 代码(
重排
给出的字符串只有每个字符有多少个有用,不妨将这些字符串按照从大到小的顺序放在一个栈里。
我们发现,将不是最小的串,直接接在最小的串后面,一定是不劣的。因此我们一直操作,直到非最小串 \(>\) 最小串。
现在我们手玩一下,发现需要合并一些最小串,使得有足够的非最小串可以接在最小串后面,并且是枚举一个值 \(x\),将前面若干个 \(x\) 个最小串一组合并在一起,后面 \(x-1\) 个一组合并在一起。
把上面一个过程写得优一点,比如合并两个串的时候时间复杂度为 \(O(\min(|a|,|b|))\),复杂度就是 \(O(n\log n)\) 的。
Round 8
这场有点摆,有空再订正。
基础寄术练习题
首先我们考虑 \(k=1\),容易归纳暴力代数证明,如果你在 \(m\) 个中选定了 \(a_1,a_2,\dots,a_n\) 个,那么其所有排列的答案之和为 \(\frac{1}{\prod\limits_{i=1}^{n}a_i}\)。
但是如果要推广到 \(k=2\),就貌似不太能代数了。那么这个东西有什么组合意义呢?
还是回到 \(k=1\) 的情况,如果我们已经确定了这个序列 \(a\),则这个式子的意义是,将总共 \(n\) 种颜色的球,第 \(i\) 种颜色是 \(a_i\),有 \(a_i\) 个,这 \(\sum a_i\) 个球排成一排,第一个球是 \(a_n\) 种颜色,第二种出现的颜色的球是第 \(a_{n-1}\) 种颜色,第 \(i\) 种球是 \(a_{n-i+1}\) 种颜色的概率,再除以 \(\frac{1}{\prod\limits_{i=1}^{n}a_i}\)。
那么在 \(k=1\) 的时候,容易得出其所有排列的答案之和为 \(\frac{1}{\prod\limits_{i=1}^{n}a_i}\),在 \(k=2\) 的时候,我们枚举 \(a_1\) 是什么,然后计算满足上面条件的排列方案数。
但是直接算还是困难,考虑容斥,枚举一个集合在 \(a_1\) 后面出现,可以设 \(dp_{i,j,k}\) 表示选到第 \(i\) 个,选了 \(j\) 个,枚举在 \(a_1\) 后面的集合总和为 \(k\)。可以 \(O(n^2m^2)\) dp,加上枚举 \(a_1\) 做到 \(O(n^3m^2)\)。
但是发现没有必要枚举 \(a_1\),直接开个 \(0/1\) 记录有没有选过 \(a_1\) 即可,可以做到 \(O(n^2m^2)\)。
【模板】矩阵快速幂
首先判掉 \(k\leq 2n^2\) 的部分,然后猜想其肯定在某个最小比例环上转若干圈,最后走到终点。
根据小凯的疑惑,如果我们找到两个互质的环,那么在 \(ab-a-b\) 之后,就可以走出任意长度。因此,开始的时候走 \(n^2\) 步,结束的时候走 \(n^2\) 步,就可以涵盖到所有情况。
我们先跑出 \(f_{i,j}\) 表示走 \(i\) 步到 \(j\) 的最小值,再求出经过每个点的最小环,然后剩下 \(n^2\) 步再用最短路推到终点即可,时间复杂度 \(O(mn^2)\)。
但是需要写高精,或者手写一个分数类,反正感觉是 dirty work,所以不写了(
Round 9
最短路求和
感觉数据强度有点过分。
我们发现 \(m-n\) 很小,所以我们可以考虑先跑个 dfs 树出来,然后对关键点建立虚树。
先对关键点跑全源最短路,并且处理掉不在虚树上的边的贡献,这样两个点之间的点就可以 \(O(1)\) 计算。
在现在的数据下这样写就能过了,实际上正解也不困难,只需要枚举虚树上一条边,然后双指针计算贡献即可。
时间复杂度 \(O((m-n)n)\)。
通道建设 Passage Construction
交互题,喜提 17 分!
Tree Topological Order Counting
首先我们考虑一个 \(O(n^3)\) 的 dp:设 \(dp_{x,i}\) 表示我们要考虑的点在 \(x\) 子树内,排除掉子树外的点的之后排名第 \(i\) 个,容易转移。
然后发现,如果我们能用 \(siz_y(siz_x-siz_y)\) 的代价从 \(x\) 转移到 \(y\),那么总体复杂度就是 \(O(n^2)\) 的,发现是可以实现的,只需要计算其它子树的贡献就行。
Round 10
雷同
首先我们发现这个题和 NOI 那个题长得很不一样,所以大概是有多项式做法的(
将 \(a\) 排序,则 \(a\) 从小到大对应深度从大到小。从下到上考虑每一层的深度,当我们要给两个点合并的时候,会计算较短的链的磨损度,同时加上子树内所有点的权值之和。
当我们确定每个点的深度时,我们会希望磨损度之和尽量小,假设对于某一深度,有磨损度 \(l_1<l_2<l_3<l_4\) 这四条链,我们肯定会让 \(l_1,l_2\) 合并,\(l_3,l_4\) 合并,这样到上一层的链的磨损度之和会尽量小。因此,对于每一层,合并链的策略是:将其按照磨损度(也即深度)从大到小排序以后,相邻两个点合并。
因此,我们考虑设 \(f_{i,j}\) 表示放了前 \(i\) 个点,当前层有 \(j\) 个点,的最小贡献,转移可以考虑新增一个点,也可以当 \(j\bmod 2=0\) 的时候,两两合并到下一层,可以做到 \(O(n^2)\)。
水果茶
不懂啊,出题人 std 真的跑得过极限数据吗?
树上长剖是平凡的,对于仙人掌,我们先跑出圆方树,然后圆点直接合并方点儿子就行,方点处合并 dp 数组也是平凡的,合并答案只需要在环上双指针一圈即可。时间复杂度 \(O(n)\),但是跑不过极限数据捏,于是我加了个特判过了这个题(
茧
找规律,找规律,找规律,然后就过了。没啥意思。
Round 11
挑战积和式
设当前的 \(a\) 总和为 \(A\),下标乘积为 \(B\),则如果给 \((A,B)\) 加上一个 \((a_i,i)\) 之后,\(iB-B\geq a_i\),则加上这个是没有意义的,也就是说, \(B\) 的乘积不会超过 \(2\times 10^9\) 。
可以证明,将 \(B\) 拆成两个数的乘积,最大的不会超过 \(\max(n,V^{\frac{2}{3}})\), 所以设 \(f_{i,j}\) 表示乘积为 \(i\),选了 \(j\) 个的最大和,合并可以斜率优化合并。复杂度可以做到 \(O(V^{\frac{2}{3}}\log V\min(k,\log V))\)。但是这样还是过不去,发现对于 \(i\),\(j\) 的乘积不会超过 \(i\) 的质因数个数,于是可以卡这个上限做到 \(O(V^{\frac{2}{3}}\log V\log\log V)\)。
往日之影
连单位根反演都没想到/oh
设 \(i\) 的最终度数为 \(d_i\),要求度数为 \(c_i\),则贡献为 \(\prod\limits_{i=1}^{n}\frac{1}{4}\sum\limits_{j=0}^{3}\omega_{4}^{(d_i-c_i)j}\)。
将这个求和看成随机对每个 \(i\) 赋值 \(a_i=\omega_{4}^{i}\),其中 \(i\) 在 \([0,3]\) 中随机,那么就变成求 \(\prod\limits_{i=1}^n a_i^{-c_i}\prod\limits_{1\leq i< j\leq n}{1+a_ia_j}\)。
注意到 \(a_i\) 是单位根的若干次方,所以 \(1+a_ia_j\) 容易为 \(0\)。更进一步的,如果要求这个不为 \(0\),则要满足:
- \(1,-1\) 只能出现一种;
- \(i,-i\) 每个只能至多出现一次。
这样直接搜不超过 \(50\) 种情况,于是暴力即可,甚至能做 \(n\leq 10^{18}\)。
虹
感觉这道题包括了几乎所有“想不到,但是不服”的思想,包括但不限于:
- 发现 \(19901991^2\bmod 20242024=1\)。
- 在 \(n\leq 10^5\) 时使用 bitset
- 搜一搜发现 \(\leq 43474197\)。
个人感觉有点逆天,所以就不订正了(雾
Round 12
这不是一道数据结构题
居然真的不是一道据数构结题!
首先我们考虑一棵树,枚举最小的一个点,删掉这个点之后将树分成了若干个连通块,则这些连通块的点编号应该连续,否则会相交。因此这个最小点的贡献是 \(d!\)。而对于剩下每个连通块和最小点相连的点,因为最小点和这个点有连边,并且最小点在这个连通块所对应的区间外边,所以和当前这个点相连的所有连通块也应该编号连续。因此,对于某个确定的最小点,方案数就是 \(\prod\limits_{i=1}^{n}d_i!\),对于每个最小点方案一样,因此总的方案数乘以 \(n\) 即可。
然后考虑有环的情况。同样枚举最小点,发现当考虑到一个环的时候,这个环既可以正着绕也可以反着绕,因此每个环给最后的方案数贡献 \(2\)。
这里的环可以扩展到点双的情况。如果一个点双存在一种赋值方案,那么还是 \(2\) 的贡献,因此把这个环找到以后不是环上的边没有影响。
一个点双存在一种赋值方案的条件是:将所有点能画在一个园上,且所有边不交。
然后我就不会做了。实际上这是 Yesterday once more(,用 WC 讲过的广义串并联图方法即可。
具体的,对于每个点双分开考虑。如果一个点只有两个度,那么可以把这个点的两个出边对应的点直接相连。如果一个图能满足上面的条件,那么可以不断重复这个过程直到其缩成两个点。
但是这并不是充要的,因为没有保证所有点都在一个环上,但是容易发现通过上面那个过程可以简单的得到环上所有边,因此再对这些边判一判即可。
不跳棋
点分板题,大概把每次点分治的桶存下来然后用个双指针维护就行了,复杂度 \(O(n\log n)\)。
goods
首先,容易看出,当我们选取了 \(n\) 个物品的时候,其期待值为 \({2n\choose B}\)。所以,一个简单的做法就是,在 FWT 的时候维护一个多项式,表示选了若干个的方案数,点乘之后再 IFWT 回去,这样复杂度是 \(O(2^mmn^2)\) 的,可以喜提 \(20\) 分。
第一个容易想到的优化是 IFWT 回去的时候没有必要对着多项式 IFWT 回去,只要能算出方案数就可以直接对着这个方案数 IFWT 了,尽管并没有什么实质性的复杂度上的优化(
但是这给我们一点启发,如果能直接算出 FWT 后方案数的点值,就可以规避直接对着多项式 FWT 了。
我们可以先 FWT 一遍,得到每个点值位置是由多少个 FWT 系数贡献为 \(1\) 和系数贡献为 \(-1\) 组成的,那么现在问题变成:有 \(n\) 个球,前 \(i\) 个系数为 \(-1\),后 \(n-i\) 个系数为 \(1\),你需要选出若干个球,假设你选了 \(j\) 个球,则贡献为 \(2j\choose B\) 乘上系数之积。
直接这样做没前途,我们考虑钦定若干个位置出现在 \(B\) 个里面,则对于剩下的球,前 \(i\) 个球的贡献为 \(0\),后 \(n-i\) 个球的贡献为 \(2\),因此我们可以发现,前 \(i\) 个必须都出现在 \(B\) 里面,否则贡献为 \(0\)。
这样就比较好做了,我们枚举后 \(n-i\) 个出现在 \(B\) 中的个数 \(j\),则可以简单\(O(n^2)\) 计算出来贡献,然后用一次卷积就可以优化到 \(O(n\log n)\) 了。总时间复杂度 \(O(m2^m+n\log n)\)。
Round 13
天空度假山庄
首先我们观察到 \(n\geq 2k+O(1)\),因此如果我们将 \(1\to 2\) 的路径复读 \(n\) 遍,并且 \(1\to 2\) 路径中走过的每种长度的边只有一种,两端点差最大的路径小于 \(\frac{n}{2}+O(1)\),则这样的路径就是满足要求的。
注意到 \(4x+4x+3=4x+1+4x+2\),也就是说每 \(4\) 个可以回到原处,因此考虑对 \(k\bmod 4\) 分讨。\(k\bmod 4=1\) 和 \(k\bmod 4=2\) 都是简单的,只需要特殊处理最后一步即可。剩下的也不困难,因为 \([1,k]\) 的总和为偶数,所以不可能凑的出 \(1\),考虑 \([1,k-1]\) 和 \(k+1\),也可以简单构造出来。
建设终末树
首先我们做一个转化:如果最后的点在 \(x\) 处,那么将这棵树看成一棵以 \(x\) 为根的内向树,那么我们的目标就是确定边的方向。容易证明,只要一个点的出边至多为 \(1\),那么这样的树就构成了一棵内向树,所以可以用 2-SAT 前缀优化建边先处理这个限制。
然后我们考虑一个点需要放在某个连通块的限制,发现这是容易的:只需要将不在连通块的点确定方向即可。然后考虑 \(q\) 个限制,发现这要求若干个物品在树上的点构成的连通块上这些边方向一样,暴力用并查集处理即可做到 \(O(n\sum\limits |V|)\)。
合并的过程显然可以用树链剖分+线段树维护,时间复杂度 \(O(\sum|V||S|\log ^2n)\)。这里直接跑 \(1.6\times 10^7\) 个点的 2-SAT 可能会被卡常,但是我们注意到可以用简单 dfs 直接替代 tarjan 的过程,这样只需要对 \(nm\) 个点, \(2nm\) 条边跑 dfs 即可。
进一步可以利用树剖可以拆成 \(O(\log n)\) 个链前缀加上 \(1\) 个链区间的性质做到 \(O(\sum|V||S|\log n)\),但是没必要。
童话
不会 poly 不会 poly 不会 poly!
Round 14
命运
链接咕咕咕。
把这个题面读懂以后,你会发现这个题相当于要你求一棵生成树,使得从大到小排序以后字典序最小,并且和 \(s\) 相连的点恰好有 \(k\) 条。
对于这种度限制生成树,可以将和 \(s\) 相连的点看作有一个点权,这个点权是这个点和 \(s\) 的连边,要求一棵生成森林,满足恰好有 \(k\) 个连通块,并且每个连通块恰好选了一个点计算入权值。
我们可以先对去掉 \(s\) 的边跑 Kruskal 生成树,然后考虑断边。首先考虑最大的边,那么当其断了之后,会减少这个点的权值,加上断了之后两边的最小点权较大的那个。然后是次大的,以此类推。容易发现这个就是 Kruskal 的逆过程,因此直接在 Kruskal 的时候处理即可,这样会得到若干可以交换的点权,排序后取最小的若干个即可,时间复杂度 \(O(m\log m)\)。
submission 鸽了。
崩坏天际线
链接咕咕咕。
出题人被爆标了/cf
首先可以把题目转化成:初始每个区间权值为 \(1\),如果某个点在区间中间,那么将这个区间按照这个点裂成两个区间,每个区间的权值是原来的一半。最后的权值是所有区间权值的总和。
我们首先考虑 sub3 怎么做。可以发现,在进行若干次操作后,会有若干个点在序列上。所有区间满足都在相邻的两个点之间。可以发现,区间数量是 \(O(q)\) 级别的。具体来说,分三类讨论:
- 没有被操作过的区间是只有 \(q\) 个的。
- 如果区间的某个端点不是划分的点,这样的区间只可能是每个初始区间最头上和最尾上的两个区间,不超过 \(2q\)。
- 否则,区间两个端点都是划分的点,则这样的区间只有 \(q\) 个。
则我们用一棵线段树维护还没有被处理的区间,用一个 set 维护两端点都是划分点的区间,用一棵文艺平衡树维护一个端点是划分点的区间,分裂的时候在平衡树上提取区间即可,这样可以做到 \(O(n\log n)\)。
然后我们又发现这个数据范围可以根号 log,于是考虑操作分块,设块长为 \(B\)。块内暴力分裂,复杂度 \(O(qB\log q)\),块间用上面 sub3 的做法维护,复杂度 \(O(\frac{q^2\log q}{B})\),则平衡得到复杂度 \(O(q\sqrt q\log q)\)。
这就是出题人给的做法了,但是为啥我们说出题人被爆标了呢?因为这题是有 \(O(q\log ^2q)\) 做法的。
具体的,我们需要证明一个结论:即使不满足 sub3 的限制,区间个数也是 \(O(q)\) 的。
为了证明这个结论,我们只需要证明第三类区间在这种限制下也是 \(O(q)\) 的即可。上面我们证明 \(O(q)\) 的条件是区间互不相交,如果放宽成区间互相包含或互相不交也只有 \(2q\) 个区间。
假设存在区间 \([x,y],[l,r]\),满足 \(x<l<y<r\),且都是第三类区间,则显然 \([x,y]\) 出现时间要早于 \([l,r]\),且中间有一次分裂 \(y\)。同理可以推出 \([l,r]\) 出现时间早于 \([x,y]\),且中间有一次分裂 \(l\),矛盾。因此不存在非包含的相交区间。
那么直接分治,用分治区间右边的划分点更新左边,复杂度就是 \(O(q\log ^2q)\) 的,但是真的跑得很慢。不知道有没有 \(O(q\log q)\) 的方法呢?
submission 咕咕咕。
Since A Light
看不懂题解(
Round 15
括号
首先考虑怎么转化成一个可维护的形式,先考虑将右括号调整成左括号,则从左往右扫,将左括号看成 \(1\),右括号看成 \(-1\),则每个前缀和为 \(-1\) 的位置,代表这个前缀需要把一个左括号变成右括号。对于左括号变成右括号同理。同时我们发现这两种修改是互相独立的,因此可以转化为:
- 有若干个位置,从左往右每个位置从前面选一个出来删了,要求最后删了的数之和最小。
考场上一点不会做,出来后发现这不是我们联合省选 D1T3 吗?具体的,你维护现在在答案中的是哪些数,插入的时候只需要找到一段前缀使得在答案中的数与需要操作的位置个数一样,那么这段前缀不能改变。其它都会和插入的位置比大小,选一个最大的扔了就行,可以线段树分治做到 \(O(q\log q\log n)\)。
但是没有必要线段树分治,实际上是可以几乎一样地把一个数给删了的:找到一段最长的后缀使得在答案中的数与需要操作的位置个数一样,这个后缀是不能再加进去数的,其它位置都可以加,取个最小的就行。
线段树容易维护,时间复杂度 \(O(q\log n)\)。
submission 鸽了。
染色
不懂啊,std 怎么要分治?怎么要 2log?
首先单位根反演,看成给每个数随机赋值 \(1\) 和 \(-1\),枚举行的 \(-1\) 个数 \(x\) 和列的 \(-1\) 个数 \(y\),发现对答案的贡献可以写成:\((-1)^x(-1)^yS\),其中 \(S\) 表示在 \(xy+(n-x)(n-y)\) 个 \(1\) 和 \(x(n-y)+y(n-x)\) 个 \(-1\) 中抽 \(k\) 个出来的乘积之和。
直接暴力枚举 \(1\) 和 \(-1\) 各选了几个可以做到 \(O(nmk)\)。我本来以为这个还要构造一下双射之类的,结果直接卷积就没了。时间复杂度 \(O(nm\log (nm))\),只跑了 100ms。
submission 鸽了。