暑假训练记录
SDSC
首先是 SDSC 好题选做。
SDSC D1T1
有一个长为 \(n\) 的序列 \(a\),给出 \(k\),你可以选择一段加上 \(k\) 或者不选。问整个序列的最大 \(\gcd\) 是多少。
\(T\leq 5,n\leq 5\times 10^5,a_i,k\leq 10^{18}\)。
答案一定是前缀 \(\gcd\) 和中间 \(+k\) 的 \(\gcd\) 和后缀 \(\gcd\) 拼起来的,而前后缀 \(\gcd\) 只有 \(O(\log V)\) 段。调整法可以发现操作的区间端点一定取到前后缀 \(\gcd\) 变化的地方,于是可以扫一遍直接求。\(O(n\log V)\)。
SDSC D1T2 CF1801D
不难观察到每次打工的 \(a_i\) 不降,否则调整法可以做到更优。这样可以设计一个 DP,设 \(f_{u,v}\) 为一个二元组 \((x,p)\) 表示目前在点 \(u\),之前经过的城市工资最高的是 \(a_v\),打工了 \(x\) 天,剩余 \(p\) 块钱,比较的时候直接按关键字依次比较,因为越往后走可能遇到的工资就越高。
用 Dijkstra 搞这个过程,\(O(nm\log m)\)。
思考:Dijkstra 搞 DP 很常见。
SDSC D1T3 QOJ7884
不难观察到划分的连通块一定是若干条链,且链的端点一定是最大和次大。这样可以设计一个 DP,设 \(f_{u,i}\) 表示 \(u\) 子数内留一条链端点权值为 \(i\) 的最大权值和,\(g_u\) 表示 \(u\) 子树内全部划分的最大权值和,转移考虑加入一个子树:
发现可以线段树合并简单维护。\(O(n\log n)\)。
思考:别忘了线段树合并。
SDSC D1T4 CF1540D
\(b\) 和 \(p\) 一定形成双射。考虑怎么刻画 \(p_i\),发现问题形如先令 \(c=i-b_i\),然后对于 \(j=i+1,i+2,\cdots,n\),若 \(j-b_j\leq c\),则 \(c\leftarrow c+1\)。
可以直接分块维护,每一块形如一个分段函数。\(O(n\sqrt n\log n)\)。
\(O(n\sqrt{n\log n})\) 还没会。
思考:像这种形式的信息并非是不能维护的,可以直接分块。
SDSC D2T1 QOJ9126
首先长度不同的字符串一定不相同,考虑固定区间的长度。进一步可以推出若区间 \([l,r]\) 与 \([l+x,r+x]\) 的字符串相同,则删除 \([l,l+x)\) 和 \((r,r+x]\) 的字符串本质相同。
然后可以发现令 \(s\) 与自己偏移 \(x\) 个字符匹配,答案需要加上不等的字符个数。拆贡献发现每对相等的字符都会匹配上一次。\(O(n)\)。
思考:拆贡献。
SDSC D2T2 QOJ10278
以每个黑点为起点在原图上跑多源 BFS 并标上其属于哪个黑点,容易发现两个黑点之间的边属于最小生成树当且仅当这两个黑点所管辖的区域相邻。
于是把这些边拎出来跑 Kruskal 即可。\(O(m\log m)\)。
思考:如果题看起来不太能做的话考虑依靠性质削一些数据范围。
SDSC D2T3 GYM105992E
考虑线段树维护。先考虑只有操作 2 咋做。
线段树节点维护区间 \(\operatorname{lcm}\) 和区间和,如果 \(\operatorname{lcm}>V\) 就设成 \(+\infin\)。修改的时候如果 \(x|\operatorname{lcm}\) 就跳过,否则一直递归到叶子节点修改。一个点只会被取 \(O(\log V)\) 次 \(\gcd\)。复杂度 \(O(n\log n\log^2 V)\)。
再考虑有操作 1。每个节点额外维护一个全相等 tag。这样加点特判做也是三个 \(\log\) 的。
考虑势能分析,定义总势能为 线段树上节点全相等且其父亲不全相等的 \(\log v\) 之和。操作 \(1\) 最多影响 \(O(\log n)\) 个节点,所以势能增加 \(O(\log n\log V)\)。操作 \(2\) 最多把端点位置的区间劈开,势能增加 \(O(\log V)\)。操作 \(2\) 每操作一个节点势能至少减 \(1\)。所以复杂度为 \(O(n\log n\log^2 V)\)
SDSC D2T4 P4845
先给出一个结论:如果 \(k\times r\geq n\),那一定存在一个方案覆盖所有点。
证明如下:考虑只需要让每个被选中的点至少覆盖 \(r\) 个点,那么考虑在树上从下往上贪心,设子树内最远的没被覆盖的点的距离为 \(k\),只要 \(k\) 达到 \(r\) 了就进行一次覆盖,这样就满足了每个点至少覆盖 \(r\) 个点。
所以可以尝试设计一个 \(O(nkr)\) 的算法,考虑 DP。然而发现有点难,因为 \(r\) 邻域贡献很难计算,于是想到延迟或提前贡献。首先子树内选了多少个点是一定要记上的,然后子树内选的最近的点的距离也要记上。尝试提前贡献,提前确定哪些点要被子树外的点覆盖,这样还需要记录一个子树内已经确定要提前贡献且还没被贡献的点的最远距离。但是这样状态数就是 \(O(nkr^2)\) 的,显然不行。
动用你的注意力注意一下,可以发现如果子树内还有已经确定且没被贡献的点,那子树内最近的被选中的点的距离这个信息是没用的,因为子树外肯定还有一个点能覆盖上。所以可以设 \(f_{u,i,0/1,j}\) 表示 \(u\) 子树内选了 \(i\) 个点,是否有还没被贡献到的点,如果有,\(j\) 表示最远的没被贡献的点的距离,如果没有,\(j\) 表示最近的选中的点的距离。
这样状态数是 \(O(nkr)\) 的。单次转移可以通过前后缀最大值优化到 \(O(kr)\)。\(k\) 这一维受限制,套用树形背包复杂度分析可以得出复杂度为 \(O(nkr)\)。
思考:如果遇到看起来就不是很可做的题可以考虑猜结论削减数据范围;提前确定贡献;树形背包复杂度。
SDSC D3T3
给定 \(n\) 个开区间 \((l_i,r_i)\),保证存在一个整点 \(x\) 满足 \(x\) 被所有区间包含。现可以花费 \(1\) 的代价选定一个 \(i\),然后把区间 \((l_i,r_i)\) 变为 \((l_i+1,r_i+1)\) 或 \((l_i-1,r_i-1)\)。问最少花费多少代价使两两区间交集为空。
\(n\leq 5000\)。
首先注意到最终状态一定是区间紧密排列,所以答案只跟线段的排列顺序有关。
先找出来那个 \(x\),把 \(x\) 平移到原点。如果 \(n\) 是奇数,那么考虑这个排列最中间的线段,左右稍微移动,两边线段的贡献不会改变,所以最中间的这个线段不会移动。如果 \(n\) 是偶数,新加上一个区间 \((0,0)\) 就行了。
枚举一下中间线段,感受一下可以发现,左侧一半线段长度一定逐渐递减,右侧一半一定递增。
写出答案式子,设中间线段编号为 \(m\),左边编号从右到左是 \(a_i\),右边编号从左到右是 \(b_i\):
从这里利用排序不等式也可以看出上面关于区间长度的结论。
所以按照区间长度排序,然后可以设 \(f_{i,j,0/1}\) 表示前 \(i\) 个线段选了 \(j\) 个在左边的最小代价,有/没有选中间的线段,可以做到 \(O(n^2)\) DP。
思考:平移可以把特殊问题一般化;找一个基准点;列出式子方便拆贡献。
SDSC D3T4
给定一个 \(n\times m\) 的网格图,每个格子有以下三种情况:
- 只能往右走。
- 只能往下走。
- 可以往右往下走。
\(q\) 组询问 \(a,b,c,d\) 问从 \((a,b)\) 和 \((c,d)\) 两个起点开始,最少总共走多少步才能会合。
\(n\times m\leq 10^6,q\leq 10^5\)。
考察从 \((a,b)\) 能走到 \((c,d)\) 的条件。
- 首先把所有 \(x+y=c+d\) 的格子拎出来,算出从 \((a,b)\) 出发能到的最靠上和最靠下的格子,那么 \((c,d)\) 一定要夹在这两个格子中间。
- 以 \((c,d)\) 为结尾的最长路长度要大于 \(c+d-(a+b)\)。
然而这两个必要条件合在一块就充要了。可以把第一个条件想象成一个轮廓,那只要满足第二个条件,就一定能碰到轮廓,然后可以顺着轮廓走过去。
考虑正解,枚举 \(k\) 表示 \((a,b)\) 走的步数,这样可以把 \((c,d)\) 走的步数算出来。考虑在 \(x+y=k\) 这条斜线上 \((a,b)\) 和 \((c,d)\) 能走到的最靠上最靠下的四个点。如果这个轮廓相交了那么一定能到达,如果相离那一定不能到达。如果包含,能到达当且仅当这四个点中至少存在一个点能让这两个点同时到达。
为什么呢,考虑找出在里面的那个轮廓,包含在这个轮廓里面的点的最长路一定不会长于轮廓上的点的最长路。
于是二分 \(k\),倍增求轮廓端点,\(O(N\log N+q\log^2N)\)。
思考:神人。
SDSC D4T4
给定一个长为 \(n\) 的环,每个点有点权 \(a_i\)。维护一个 DS 支持区间赋值区间加以及查询:
给出一点 \(p\) 和初始权值 \(v\),一个人从 \(p\) 开始移动,设 \(mx\) 为遇到过的点的点权最大值,则经过点 \(i\) 后权值减少 \(mx\times a_i\),问停下的点的序号。
考虑到走过了一整圈之后最大值就不变了,那么可以只考虑最多走一圈。
发现有前缀最大值,考虑单侧递归线段树。如果没有区间加,可以方便地统计出来区间所需要耗费的权值,线段树二分即可解决。
如果有区间加,维护区间加标记,发现标记 \(+c\) 作用到信息上是 \(\sum (mx_i+c)\times (a_i+c)=\sum mx_ia_i+c(a_i+a_i)+c^2\),那么还需要维护一个区间和和区间 \(\max\) 和,直接维护就好了。
复杂度 \(O(\log^2 n)\)。
思考:遇见前缀 \(\max\) 可以考虑单侧递归线段树;用线段树维护东西要思考信息合并,标记合并,标记作用到信息上。
SDSC D5T4
给定一个长为 \(n\) 的整数序列和一个阈值 \(w\),你要重排这个序列满足相邻两项乘积不超过 \(w\)。问方案数。
- Subtask 1:\(n\leq 2000\)。
- Subtask 2:\(n\leq 50000,a_i\geq 0\)。
对于所有数据保证 \(-10^9\leq a_i\leq 10^9\)。
考虑类似线头 DP,加入每个数可以选择新开一段或接在原来的段上。但是发现限制不好处理。考虑根号分治,大于 \(\sqrt w\) 的分一组,剩下的一组。于是大的不能跟大的匹配,小的可以跟小的匹配,但是大小之间不确定。
回忆一下以前做过的连续段 DP 题,一般都是先钦定一个顺序然后再 DP。对于这道题也考虑构造一个顺序,数分成两类,一类是加入时前面所有数都能跟他匹配,称为 A 类,一类是前面所有数都不能跟他匹配,称为 B 类。转移的时候,A 类可以新开一段也可以接上之前的,B 类只能新开一段。
考虑如下构造方式:把大元素 \(y_i\) 升序排序,对于每个小元素 \(x\) ,找到满足 \(xy_i\leq w\) 的最大的 \(i\) 将其插入到这个位置。容易发现这样可行。
于是现在我们就会了 \(a_i\geq 0\) 的 \(O(n^2)\) 做法。不能通过任何一个 Subtask。
若存在 \(a_i<0\),容易知道正负数之间一定能匹配。把正负数分别拎出来做上面那个 DP,最后交替合并即可。\(O(n^2)\)。
考虑 \(n\leq 50000,a_i\geq 0\),此时这么 DP 肯定没有前途。考虑把加入元素的序列倒过来,这样变成了要么后面所有元素都可以匹配,要么后面所有元素都不能匹配。在依次加入的过程中维护有多少个可以插进去的空位,若来一个 A 类元素空位个数 \(+1\),来一个 B 类空位个数就 \(-1\),这样方案数容易 \(O(n)\) 计算。
数据开 \(50000\) 应该是给奇怪分组方法过的。
思考:线头 DP;构造顺序;逆向思维。
SDSC D6T1
给定一个长为 \(n\) 的在 \([-1000,1000]\) 中均匀随机的数列 \(a\) 和正整数 \(k\),选出 \(k\) 个不相交的子段满足子段长度为质数,最大化每段和的最小值。
\(n\leq 2\times 10^5\)。
看到这么奇怪的东西不妨把奇怪的地方联系起来:均匀随机,质数。于是想到质数分布,每 \(O(\log n)\) 个数中有一个质数。
考虑二分答案,如果发现一个段能选就贪心地选上,用 set 维护待选集合,每次暴力查找,期望在 \(O(\log n)\) 次查找中找到一个质数,所以复杂度是 \(O(n\log^2 n)\) 的。
思考:随机性质;质数分布。
SDSC D6T2
给定 \(n,m,k\),问有多少个长为 \(m\) 的 \(k\) 维超立方体序列满足每个超立方体的每一维都在 \([1,k]\) 之内且任意两个超立方体无交。
\(n,k\leq 10^9,m\leq 6\)。
考虑为什么难算,因为两个超立方体不相交的条件是存在一维不相交,存在性不好搞,所以容斥。容斥钦定一些对一定相交,相交的条件是所有维都相交,这样可以只算出一维的答案再做 \(k\) 次幂就是整个的答案。
考虑一维的问题,即区间问题,钦定一些对相交了。然后可以做一个离散化 DP,设 \(f_{i,S}\) 表示目前是离散化的第 \(i\) 个位置,包含这个位置的线段集合,有没有出现过等各种信息为 \(S\),然后可以状压 DP,最后乘上个组合数就是答案。复杂度大概是 \(O(m2^{\frac{m(m-1)}{2}}5^m)\),反正能过。
SDSC D6T3
对于一个长度为 \(n\) 的排列 \(p\),令 \(p_0=p_{n+1}=+\infin\),记 \(l_i\) 为排列中 \(p_i\) 左边第一个比它大的数的位置,\(r_i\) 为右边。定义 \(p\) 的权值 \(f(p)=\sum_{i=1}^n\min\{i-l_i,r_i-i\}\)。
一开始给定质数模数 \(P\),然后又有 \(T\) 组询问,每次给定 \(n,x\),问有多少个长为 \(n\) 的排列 \(p\) 满足 \(f(p)=x\)。
\(T\leq 10,n\leq 300,x\leq 10^9\)。
排列比大小可以放到笛卡尔树上来做,发现 \(\min\{i-l_i,r_i-i\}\) 就是 \((l_i,r_i)\) 这个区间对应的树上节点的左右子树中较小的那一个。通过启发式合并可以得知 \(f(p)\) 的量级为 \(O(n\log n)\),所以 \(x\) 范围很大纯属诈骗。
容易发现排列与笛卡尔树形成双射,考虑对这个笛卡尔树 DP。设 \(f_{i,j}\) 为 \(i\) 个节点的笛卡尔树权值为 \(j\) 的方案数。转移考虑根结点一定是最大值,枚举左右子树的信息然后合并:
可以发现转移的后面一个求和号相当于卷积,拿生成函数表示一下。设 \([x^j]F_i(x)=f_{i,j}\),那么有:
发现这总共 \(n\) 个多项式有用的点值总共有 \(O(n^2\log n)\) 个且可以在 \(O(n^3\log n)\) 的时间内求出,那么可以求出点值之后拉格朗日插值把多项式插出来。
思考:遇到卷积形式的递推式可以用生成函数表示然后算点值最后拉插。
SDSC D6T4
还没搞懂。
接下来是一些 Round。
CF 2129
B
考虑算贡献,让值小的去算值大的的贡献,发现值大的不管怎么选都不会影响到贡献,所以贪心即可。
思考:考虑值域;考虑消除影响。
C
首先用二分确定出任意一个左右括号的位置,然后考虑拿这两个括号去构造一个模板串。
C1,可以构造出 (x(()(y,这样返回值为 \(1,2,3,4\) 分别对应着 \(4\) 中不同的可能方案。
C2,可以构造出 (x ( ((y) ( ((((z))) ( ... 给返回值二进制分解,这样可以一次验证 \(9\) 个括号。
C3,做法已经明了了,就是构造出 \(t\) 个带一个空缺的括号序列,设第 \(i\) 个括号序列的空缺填上 ( 可以得到 \(a_i\) 的贡献,那么要求 \(a_i>\sum_{j=1}^{i-1}a_j\)。观察得到 ...()()()(x()()()... 这样的括号序列贡献最多,于是贪心一下可以得到一次验证 \(13\) 个括号的序列,可以通过。
思考:贪心构造。
D
首先观察可以发现,\(s_i\) 为 \(O(\log n)\) 量级。
这种看上去不会做多项式复杂度的需要消除后效性。注意到如果一个区间的两头都被染黑了,那么区间内外无法互相影响。
那么可以设计一个区间 DP,设 \(f_{l,r,x,y}\) 表示,不妨先设 \(l-1\) 和 \(r+1\) 已经染黑,把 \([l,r]\) 全部染黑,对 \(l-1\) 贡献 \(x\),对 \(r+1\) 贡献 \(y\) 的方案数。状态数是 \(O(n^2\log^2n)\) 的。转移时枚举一个分割点表示最先操作的那个位置,答案即为 \(f_{1,n,0,0}\)。
复杂度 \(O(n^3\log^3n)\)。
思考:无后效性;区间 DP。
E
先用莫队,但是复杂度不对,因为加点的复杂度是 \(O(deg)\) 的。
但是我们注意到 \(\sum deg=O(m)\),所以按 \(deg+1\) 来搞莫队的分块复杂度就全对了。
思考:莫队复杂度。
CF2127
A
省流:\(\operatorname{mex} \neq 0\Leftrightarrow \min =0\);\(\operatorname{mex}\neq \max\)。
B
容易发现一些东西然后就做完了。
C
注意到答案只会增加不会减少,于是拿 set 找一找就做完了。
D
- 原图是一棵树,如果有环方案数为 \(0\)。
- 容易发现这棵树的形态是一个类毛毛虫结构,否则方案数为 \(0\)。
做完了。
E
先考虑没有 \(0\) 怎么做,需要一个启发式合并来算贡献。
考虑有 \(0\),那么可以发现子树内颜色种类一定不会增加,否则不优,于是可以算出这个数的贡献。做完了。
F
先翻译一下 \(f(a)\) 是啥。设 \(t_i\) 为 \(a\) 序列的最大值的位置,则:
肯定考虑拆贡献。在拆贡献之前,先考虑怎么解决如下问题:
给定 \(n,m,x\),求有多少长度为 \(n\) 的序列 \(a\) 满足 \(a_i\in [0,x],\sum_i a_i=m\)。
发现这个题有点像硬币购物。回顾一下那个题,那个题是容斥原理钦定一个集合不满足,那这个也得用容斥。限制相同考虑二项式反演:
设 \(f_i\) 表示恰好 \(i\) 个限制不满足的方案,\(g_i\) 表示钦定 \(i\) 个限制不满足的方案,则有:
答案即为 \(f_0\),设其为 \(f(n,m,x)\):
注意到 \(i\) 的上界是 \(\lfloor\frac{m}{x+1}\rfloor\),所以这个算法的时间复杂度是 \(O(\frac{m}{x})\)。
接下来来拆贡献,把所有能拆的拆掉:
从 \(0\) 到 \(m\) 枚举最大值,设枚举的为 \(t\)。
先来解决第一项。已知 \(a_n\) 一定是最大值,所以先加上 \(t\times f(n-1,m-t,t)\),再算上前面的,\(t\times (n-1)\times f(n-2,m-2t,t)\)。
再来解决第二项,直接做不太好做。这里考虑对称性,如果能算出来所有方案中前 \(n-1\) 项的和,那么除以 \(n-1\) 之后就是答案。而这个是好算的,就是 \((m-t)f(n-1,m-t,t)\)。
最后考虑第三项,根据上面的推导可以直接随便算算。
于是复杂度是调和级数的 \(O(m\log m)\)。
接下来是一些 CF 2k5 Counting。
CF 2k5 Counting
CF1909F2
考虑贡献延后。也就是说 DP 到 \(i\) 的时候只填值域 \([1,i]\) 的数,这样就好做了。
思考:贡献延后;值域。
CF1854C
感觉这题不简单。
先拆贡献,根据期望线性性,总消失步数是各消失步数之和。把贡献拆到每个元素上,发现其在走 \(m-x+1\) 步之后一定消失,所以可以先给一个基础贡献 \(m-x+1\)。
然后发现如果两个元素在 \(x\) 位置相遇,那么贡献会减少 \(m-x+1\)。而且这种情况只可能会在相邻的两个元素中出现,所以可以把贡献拆到每相邻两个元素上。
这样我们要算的是,给出 \(x,y,z\),问两个物品从 \(x,y\) 出发到 \(z\) 相遇的概率。可以用 DP,设 \(f_{i,j}\) 表示两个物品分别在 \(i,j\) 的概率,转移很方便。这样就做到了 \(O(n^3)\)。
然而因为期望的线性性,可以把这 \(n\) 个相邻的元素一块扔进去 DP,就做到了 \(O(n^2)\)。
思考:期望线性性;拆贡献。
CF1737E
所谓算概率其实是算方案数。
考虑如何对一种方案求出谁能活到最后。先把整个序列分段,让每一个向左的蚂蚁跟他左边一串向右的蚂蚁分成一段,这样进行一些合并之后所有蚂蚁都向左且大小为所在段长。然后从左往右挨个判断即可。
根据上面所说的我们可以发现一个和为 \(n\) 的正整数序列可以与一个方向方案双射,设这个序列为 \(s\)。那么 \(i\) 能吃掉左边与右边互相独立。左边可以直接算,右边用 DP 就行了。\(O(n)\)。
思考:双射;思维。
CF1725E
考虑拆贡献,\(f(x,y,z)=\frac{1}{2}(d(x,y)+d(y,z)+d(x,z))\),\(g(x,y,z)=\sum_{p\in \text{prime}}[p|a_x][p|a_y][p|a_z]\)。要算:
考虑给每个质因子建虚树分开做,这样问题规模变为 \(O(n\omega(n))\),问题变为求 \(\sum_{x<y}d(x,y)\),DFS 就行了。\(O(n\omega(n)\log n)\)。
思考:拆贡献,虚树。
CF1043F
首先注意到答案上界为 \(\omega(3\times 10^5)=7\)。因为如果有超过 \(\omega(V)\) 个数,则必定能拿出来一个数,使得不选这个数 \(\gcd\) 仍为 \(1\)。
然后咋做呢,我们在值域上考虑这个东西,如果能算出 \(f_{i,j}\) 为选 \(i\) 个数 \(\gcd=j\) 的方案数就很好。但是做不了。所以先求出 \(g_{i,j}\) 为选 \(i\) 个数 \(j|\gcd\) 的方案数,再容斥回来即可。
思考:答案上下界;\(gcd\) 容斥;值域。
接下来是一些杂题
杂题
CF1322F
设 \(b_i\in\{0,1\}\),\(b_i=1\) 当且仅当 \(a_i<a_{fa_i}\)。看限制,拆到每条边上形如一些边的 \(b\) 必须不等,一些必须相等。用并查集处理这个限制,顺便判一下无解。
最小化最大值可以二分,设二分的值为 \(k\),进行一个树形 DP(或者也可以说是贪心)。观察到若有一种合法的赋权方案,则将 \(a_i\) 全部替换为 \(k+1-a_i\) 也同样可行,于是可以在贪心的过程中直接钦定 \(b_u=1\)。
设 \(f_u\) 表示 \(u\) 子树内限制全部满足,且 \(b_u=1\) 的 \(a_u\) 最小值。考虑转移,枚举一个儿子 \(v\),无非分为三种情况:
- \((v,u)\) 上没限制,直接忽略。
- \((v,u)\) 上有限制且与 \((u,fa_u)\) 在同一个并查集。那么通过 \(b_u\)可以得知 \(b_v\) 的值,进而得知 \(a_u\) 的取值范围。设所有这一类的范围的交为 \([L,R]\)。
- \((v,u)\) 上有限制且与 \((u,fa_u)\) 不在同一个并查集。这些边先用并查集连通块查一下,那每个连通块就相当于一个限制 \(f_u\in[L,R]\cap([l,r]\cup [k+1-r,k+1-l])\)。
复杂度 \(O(n\log n)\)。
GYM103428C
模意义下乘法规律有点少,先找个原根然后取对数变成加法,然后写一个 DP。每次转移形如:
可以拿 bitset 做但是过不去。注意到把 \((i,(i+t)\bmod n)\) 连边之后形成若干个环,其中 \(0\to 1\) 的边个数与 \(1\to 0\) 的个数相等,我们要把所有 \(1\to 0\) 的边找出来,如果能快速把所有端点不相等的边找出来那么复杂度就是对的。
找位置可以二分 \(x\) 判断 \([p,p+x]\) 与 \([p+t,p+t+x]\) 是否相同,用树状数组维护哈希实现。\(O(n\log^2n)\)。
NOI2025 D2T2
要求的答案式子:
枚举 \(S\),钦定 \(S=f(P)=f(Q)\),因为一些数的 and 等于一个数不太好做,所以考虑容斥。
容斥之前理一下思路。
设 \(f_{X,Y}\) 为 \(f(P)=X,f(Q)=Y,P\cap Q=\varnothing\) 的 \(\prod_{i\in P\cup Q}a_i\) 的和。
设 \(g_{X,Y}\) 为 \(X\subseteq f(P),Y\subseteq f(Q),P\cap Q=\varnothing\) 的 \(\prod_{i\in P\cup Q} a_i\) 的和。
则有如下关系式:
根据子集反演,得到:
而根据乘法原理(考虑每一个元素放到 \(P\) 还是 \(Q\) 里)容易得到:
所以可以得到:
要求的答案就是:
然后可以用一个高维后缀和预处理出 \(f_S=\prod_{S\subseteq V}(\frac{1+2a_V}{(1+a_V)^2})\) 和 \(g_S=\prod_{S\subseteq V}(1+a_V)\),原式化为:
修改 \(f_S,g_S\) 的定义,原式化为:
发现后面的形式是一个 OR 卷积,直接做即可 \(O(n2^n)\)。
但是我们忽略了致命的一点,\(1+a_V\) 在分母上,而 \(a_V\) 可能等于 \(-1\)。
联合省选 2025 D2T2
直接来考虑 C 性质来。就是要我们刻画一个图存在外向生成树的条件。就是要存在一个点能到达所有点。
考虑状压 DP 来直接,设 \(dp_S\) 为 \(S\) 内点存在外向生成树的方案数,转移的时候钦定一个点集 \(T\) 能到达所有点,肯定得容斥对吧,让我们来推一下容斥系数:
设 \(f_T\) 为恰好,\(g_T\) 为钦定,其中 \(T\subseteq S\),\(E_{S,T}\) 为 \(S\) 到 \(T\) 的边数。
先看看 \(T\) 咋算,要求 \(T\) 内部是一个强连通分量呗,那就先套个主旋律上去,设 \(h_T\) 为 \(T\) 内部是一个强连通分量的方案数。那看看 \(T\) 跟 \(S-T\) 之间呢,发现
接下来要学习一些【】博客。
【】博客
AT_kupc2013_j
猜答案是关于 \(h,w\) 的二元多项式。
证明考虑容斥,条件是骨牌之间不交,那就容斥成相交,然后发现变成了若干图案在图内互不影响的随便放的方案数,答案形式显然为:
可以看出多项式次数最高为 \(n\),那么直接求出 \([n,2n]\) 的点值然后两次拉插即可。
思考:容斥证明答案为多项式;二维拉插。
P9165
通过任意的 \(n\) 个正确点值即可插值出 \(n\) 阶多项式,于是问题转变为如何传递至少 \(100\) 个正确的数并且区分出正确的数。
算出 \(150\) 个点值,每个数发 \(5\) 遍,如果众数出现次数 \(\geq 2\) 就取值,否则扔了。这样每个数无效概率是 \(\frac{3}{16}\),错误概率约等于 \(0\)。而我们有 \(150\) 个数,那很好了。
思考:将 and 变为 or。
ABC306H
做题很重要的就是要保证头脑清醒。
不妨先把等于号扔了,那就相当于给整个图定向,问是 DAG 的方案数。
再推一遍 DAG 容斥。设 \(E_S\) 表示 \(S\) 集合内部边数,\(dp_S\) 表示 \(S\) 内部方案数:
考虑加上等于号,发现钦定的这些入度为 \(0\) 的点之间的边必须全设为相等,那设 \(g_S\) 表示 \(S\) 内部连通块数:
可以 \(O(3^n)\) 计算。
思考:DAG 容斥。
必应课件
QOJ2551
考虑生成函数,直接推:
这是一个线性递推数列,矩阵快速幂直接做就行。
接下来进行一些 JOISC 的板刷。
JOISC
JOISC 2025
Day 1
T3
不难发现一个点往上跳之后能到达的点是一个连通块且能到达的点只会增加。所以一个点每次往上跳应该跳到能到的最高的那个点。这个点可以给点按点权排序然后从小到大加点并查集求出。然后发现这是一棵树的结构,可以搞一个树上倍增,这样问题变为判定一个点是否可以一步跳到另外一个点。
相当于只能走点权小于等于 \(h_x+L\) 的点看能不能到达,这是经典 Kruskal 重构树形式。直接做就行。
思考:倍增;Kruskal 重构树经典形式。
T2
想到一个 \(L=27\) 的做法。
大概就是说先构造一个模板串 10 10 10 ... 10 共 \(9\) 个 10,表示 \(9\) 个二进制位。前 \(991\) 张牌用来构造一个这样的序列,最后 \(9\) 张牌用来表示前 \(991\) 张牌中有多少 \(1\)。这样构造的好处是可以从右往左贪心,如果最右边的串是 10 就说明这一位二进制是 \(0\),如果为 100 或 101 说明是 \(1\),这样可以解码出二进制数。注意这最后 \(9\) 位如果有不用的,要添加到序列的最左端,这样才能正确计算出最后 \(9\) 个数有多少个 \(1\)。问题在于构造不出这个模板串怎么办,那一定有最终串长小于 \(27\),而且前 \(991\) 个数中 \(0\) 和 \(1\) 中必定有一个数量小于 \(9\),这样可以算出前 \(991\) 个数的贡献。
至此这个题的思路应该就明了了,就是要你用前 \(x\) 个数构造一个模板串出来,然后用剩下 \(900-x\) 个数编码。除了二进制编码,还可以用组合数编码。假设我们能发送一个长为 \(14\) 包含四个 \(1\) 的串,那么就有 \(\binom{14}{4}\) 种方案,也就是说我们要把这 \(4\) 个 \(1\) 之间的 \(5\) 个间隔发过去。
然后可以发现一个模板串长这样 00001111,如果 \(1\) 进来就把他插到 \(0\) 里面,反之亦然。则让 \(10\) 个任意的数来编码,能发送这 \(5\) 个间隔过去。
考虑这样效率很低的原因是传过去的串一定是 \(14\) 个数字,如果说能传过去 \(4\) 个 \(0\),\(1\sim 8\) 个 \(1\),那么就有 \(\sum_{i=1}^8\binom{4+i}{4}=1286\) 种,这样能做到 \(L=16\)。
如果把 \(0\) 的数量也变一下,这样就是 \(L=14\) 了。
思考:构造;不定长编码。
T1
好题。
字典序考虑贪心。一个很自然的思路是从大到小枚举每个值,设 \(c_i\) 为值 \(i\) 出现的次数,从小到大考虑每个还没确定答案的区间,记录当前选了哪些区间,如果加入当前区间后最小点覆盖 \(\leq c_i\),那就把这个区间的答案设为 \(i\)。这样直接做复杂度 \(O(n^3)\)。
这道题的核心在于,区间删完一个少一个,所以尝试设计一个均摊复杂度的算法。
考虑每次选出的区间,一定是一段前缀加上后面一些零散的区间。这个前缀有什么特殊的地方?其一定是最长的最小点覆盖 \(\leq c_i\) 的前缀。剩下的那些有什么特殊的地方?其加入后一定不影响最小点覆盖答案。于是这个题可以分为两部分,先找到这个前缀,再找到剩下的区间。
先解决第一部分。可以二分一下,判定的时候直接跑一遍最小点覆盖,但是发现直接二分复杂度不对,这要求我们设计一个复杂度跟答案相关的算法。可以先倍增后二分。具体地,倍增找出 \(k\) 满足前缀右端点在 \((2^k,2^{k+1}]\) 中,设答案(也即要删除的点的数量)为 \(a\),这一部分时间复杂度是 \(O(a\log^2 a)\)。然后在 \((2^k,2^{k+1}]\) 中二分出答案,这部分时间是 \(O(a\log a)\) 的。所以我们就解决了前缀问题。
然后考虑找后面的区间。后面这个区间合法当且仅当存在一种覆盖前缀的方案满足其中一个点覆盖了这个区间,现在当务之急就是要刻画所有覆盖前缀的方案。可以从左往右跑一遍最小点覆盖,求出第 \(i\) 个点 \(x_i\) 的最右值 \(r_i\),从右往左跑求出 \(l_i\)。可以发现一个事情是对于第 \(i\) 个点一定存在几种方案使得其取遍 \([l_i,r_i]\),实际上只要令 \(x_j=r_j(j<i),x_j=l_j(j>i)\) 即可。所以可以先跑出来这几个区间 \([l_i,r_i]\)。
然后分讨新加入的区间的位置:
- 与任意一个区间无交:这个区间不能被加入。
- 完整包含一个区间:可以直接加入,不需要修改。
- 与仅一个区间有交:可以加入,将这个区间修改为他们俩的交集。
- 与前一个区间的后缀和后一个区间的前缀有交:可以加入,但是不知道该如何修改,因为后面可能在一些 3 类区间之后将这个区间变成 3 类区间。
考虑直接把第 4 类区间的影响记下来。也就是对每个区间 \(i\) 维护两个堆,堆里存二元组 \((x,y)\),意义为如果 \(r_i<x\),就修改 \(r_{i+1}\leftarrow \min\{r_{i+1},y\}\),\(l\) 同理。修改的时候暴力递归,不难发现这样复杂度均摊正确。
现在唯一的问题在于 1 类区间会导致均摊复杂度错误。于是我们需要一个数据结构来快速找出与这些 \([l_i,r_i]\) 有交的编号最小区间。注意到复杂度是均摊的,所以可以拆到每一个区间上,也就是查询与一个区间 \([l,r]\) 有交的编号最小区间,拎出来这个区间之后就把它影响到的所有区间重新查询,当 \([l,r]\) 有修改的时候也重新查询。
但是这样还存在一个问题,就是可能存在一个区间包含很多 \([l_i,r_i]\),这样就要重新查询很多区间,复杂度是假的。实际上注意到这种区间都是 2 类区间,只需要一开始找出来就行。所以途中实际上只需要找 3 4 类区间,这样复杂度均摊正确。
最终的问题就是怎么找到这个区间,发现可以用线段树简单维护。所以这个题在 \(O((n+m)\log^2n)\) 的时间内解决了。要想优化到单 \(\log\),发现瓶颈在于第一部分找前缀判定的时候每次对区间排序,只要给每个 \((2^k,2^{k+1}]\) 单独排序再归并就行了。所以可以做到 \(O((n+m)\log n)\)。
思考:均摊复杂度;倍增二分;最小点覆盖性质;注意力。
接下来是学校模拟赛的一些记录。
学校模拟赛
8.11 glx round
A
错题,没看。
B
模板树形背包。
C
没看见是排列导致虚空思考一百万年。注意到是排列之后可以发现 \((1,n,1)\) 带来的贡献只与 \(p_1\) 这个值有关。所以要算的就是一个前缀和的 \(\min\) 状物。直接每个位置维护其前缀和,发现修改可以使用你喜欢的数据结构维护。
D
首先肯定对每个颜色分开考虑贡献。对每个颜色建虚树,然后发现可以贡献的路径放在 dfn 平面上形如一个矩形。于是是 \(O(5n)\) 次矩形加,\(O(m)\) 次单点求和。直接做就行。
8.13 hsg round
T1
梦熊题。
T2
考虑离线按 \(m\) 排序,然后中位数经典 trick,把所有数变成 \(-1,0,1\),可以写一个 DP。做每个 \(m\) 的时候动态修改一下每个点的点权,然后可以 DDP。发现不用 DDP,直接暴力搞就行,因为深度很小。
T3
数据分治,小的状压大的 DP。
T4
看到平方肯定拆贡献啊,变成算两条长度为 \(l\) 的链,设这两条链的并集为 \(S\),那么贡献为 \(k^{n-|S|}\)。
并集这个限制太难处理了,容斥搞成交集。发现你这个贡献跟集合的大小有关,所以需要搞一个二项式反演。
先搞个预处理 \(g_{u,v,i}\) 表示从 \(u\) 走到 \(v\) 走 \(i\) 步的方案数。再搞个 DAG 拓扑序 DP 设 \(f_{u,x,y,k}\) 表示当前在 \(u\) 且钦定了这个点,第一条链长度为 \(x\),第二条长度为 \(y\),一共钦定了 \(k\) 个点。转移枚举一个点 \(v\),对 \(x,y\) 这两维是一个卷积。直接转移复杂度 \(O(n^2l^5)\)
注意这两维卷积卷的是同一个多项式,所以可以分开来卷,优化到 \(O(l^3)\)(其实就算不是一个多项式也可以在不用 FFT 的情况下做到 \(O(l^3)\))。考虑再优化,注意到我们只关心所有方案的 \(k^{|S|}\) 之和,并不用必须求出每个长度的具体是多少。先推推式子,设 \(f_i\) 表示最终钦定 \(i\) 个点的方案数,根据二项式反演,答案为:
于是可以把贡献拆到每次转移上,乘上一个 \((k-1)\),这样就省掉了最后一维。
复杂度 \(O(n^2l^3)\)。

浙公网安备 33010602011771号