Codeforces 杂题 I [2000, 2900]
1322B | 2100 | C
看上去异或里面套了层加法不好拆位,但是依然可以对每个二进制位处理。
现在考虑第 \(k\) 位,那么产生贡献的数字对一定满足以下条件之一:
- 第 \(k\) 位相同且前 \((k-1)\) 位进位;
- 第 \(k\) 位不同且前 \((k-1)\) 位不进位。
那就按照前 \((k-1)\) 位排序,然后能产生进位的一定是一个后缀,所以维护一个前缀第 \(k\) 位是 \(1\) 的个数,然后双指针就能算合法的数字对个数了。
Code 13 min
900D | 2000 | B
\(y\gets \frac{y}{x}\),于是问题变成了有多少个数列满足其 \(\gcd\) 为 \(1\),总和为 \(y\).
首先如果不考虑 \(\gcd\) 的限制,答案为 \(2^{y-1}\).然后考虑容斥(莫比乌斯反演)钦定一些质因数集合是出现的,不难发现出现了的必须是 \(y\) 的质因数,假设它们构成的集合为 \(S\),乘积为 \(k\),那么对答案的贡献是 \((-1)^{|S|}2^{\frac{y}{k}-1}\).时间复杂度 \(\mathcal{O}(2^{\omega(y)})\).
1217E | 2300 | B
由于 \(x\not\equiv 2x\pmod {10}\),所以任意大小为 \(2\) 的集合平衡当且仅当每一个十进制位上至少有一个是 \(0\).
考虑任意一个大小 \(>2\) 的不平衡集合,容易猜到结论是其一定包含一个大小为 \(2\) 的不平衡子集。考虑反证,如果任意两个大小为 \(2\) 的子集都是平衡的,那么这个集合的总和在每一位上至多有一个数不为 \(0\),那么这个集合是平衡的,和假设矛盾。
所以只需要用 “十进制非零为有交的数对” 的和来更新答案即可。
对于每个十进制位开一个线段树维护区间内这一位上非零的数的最小值和次小值即可。或者一棵线段树,所有位的信息都一起维护也是一样的。
时间复杂度 \(\mathcal{O}(m\log n\log a)\).
1215E | 2300 | B
\(a_i\leq 20\) 想状压 dp,\(f_S\) 表示权值集合 \(S\) 的最小答案。
要怎样算贡献?需要处理出 \(g_{x,S}\) 表示满足 \(a_i=x,a_j\in S,i<j\) 的 \((i,j)\) 个数。也就是对于所有 \(a_i=x\),统计 \(x\) 后面有多少个权值是在 \(S\) 中,再求和是多少。则有:
为了计算 \(g\),还需要算出 \(w_{x,y}\) 表示 \(a_i=x,a_j=y,i<j\) 的 \((i,j)\) 个数。这个可以将所有 \(x\) 和 \(y\) 提出来然后扫一遍。则有:
处理出 \(w\) 的时间复杂度是 \(\mathcal{O}(na)\) 或者 \(\mathcal{O}(na^2)\),状压 dp 的时间复杂度是 \(\mathcal{O}(2^aa)\).
1188C | 2500 | D
首先转化成求美丽度 \(\geq i\) 的子序列个数 \(g_i\).
求一个 \(g_x\),直接暴力 dp 的话大概是先将 \(a\) 排序,然后令 \(f_{i,j}\) 表示前 \(i\) 个 \(a\) 选了 \(j\) 个,然后双指针扫前缀和优化转移能做到 \(\mathcal{O}(nk)\),这样总复杂度是 \(\mathcal{O}(ank)\).
没想到的:当 \(x\) 比较大的时候 \(g_x\) 实际上就是 \(0\) 了,类似鸽巢原理,如果 \((k-1)x>\max\{a\}\) 那么至少有两个数的间隔 \(<x\),所以 \(x\) 只需要枚举到 \(\frac{a}{k}\),这样时间复杂度就是 \(\mathcal{O}(nk\cdot \frac{a}{k})=\mathcal{O}(na)\).
1111E | 2500 | A
首先考虑求出 \(num_x\) 表示 \(x\) 在 \(r\) 为根的树中有多少个祖先是关键点,那么答案就可以 \(\mathcal{O}(k\log k+km)\) dp 出。
具体就是先给 \(num\) 排个序保证祖先比子孙先被计算到 dp 状态里,然后就是令 \(dp_{i,j}\) 表示前 \(i\) 个点分 \(j\) 组,那么即有转移 \(dp_{i,j}=dp_{i-1,j-1}+dp_{i-1,j}\times \max(j-num_i,0)\).
然后根号分治,考虑 \(k>\sqrt{10^5}\) 直接暴力 dfs 统计,\(k\leq \sqrt{10^5}\) 利用 \(\mathcal{O}(n\log n)-\mathcal{O}(1)\) LCA(ST 表)可以做到 \(\mathcal{O}(k^2)\) 暴力统计(用 \(dis(x,y)+dis(y,z)=dis(x,z)\) 来判断 \(y\) 是否在 \(x\to z\) 上)。
那么复杂度就可以做到 \(\mathcal{O}(n\sqrt n+\sum km)\)(假设 \(n,q,\sum k\) 同阶).
实际上根号分治也想麻烦了,可以直接看作给关键点染色,然后求关键点到 \(r\) 路径上被染色的点的个数。用树剖维护可以做到 2log.直接建虚树之后 dfs 可以做到 1log.
1097D | 2200 | B
由于各个质因数之间是独立的,所以积的期望等于期望的积。
那么求出每个质因数 \(p_i^x\) 的期望 \(E({p_i}^x)\),那么 \(\prod E({p_i}^x)\) 即为答案。
那么现在问题变成了,你有一个数 \(x\),每次操作 \(x\) 随机变成 \(0\leq y\leq x\) 的整数 \(y\),求 \(k\) 次操作后 \(x\) 的变成 \(i\) 的期望。
由于 \(x\leq \log n\),那么 \(\mathcal{O}(k\log n)\) dp 即可。然后 \(E(p^x)=\prod p^iP(x=i)\).
时间复杂度是 \(\mathcal{O}(\sqrt n+\omega(n)k\log n)\).
1044D | 2400 | B
转差分咯,然后每条信息相当于 \(s_l\operatorname{xor}s_r=x\),或者询问 \(s_l\operatorname{xor}s_r\).
然后考虑将每个 \(s\) 看作一个点,\(s_l\operatorname{xor}s_r=x\) 则连边 \((l,r,x)\).考虑如果存在一条路径 \(x\to y\),那么 \(s_x\operatorname{xor}s_y\) 就是这条路径上的边权和。那么如果新加的一条边 \((l,r,x)\) 如果 \(l,r\) 已经连通则直接忽略。
问题就变成了:每次将两个不连通的点之间新建一条带边权的边,或者询问两个点之间路径边权 \(\operatorname{xor}\) 和是多少。
因为强制在线,所以需要启发式合并然后维护倍增数组,就是 LOJ 6038 那个。时间复杂度是 \(\mathcal{O}(q\log^2 q)\).
谔谔又做麻烦了,这个信息很简单,实际上只需要用个带权并查集就可以了。记录 \(val_x\) 为 \(x\) 与 \(fa_x\) 之间路径的异或是多少,也可以路径压缩,然后需要用 map
来记录。
870E | 2300 | B
建个二分图,\((x,y)\) 存在则给行 \(x\) 代表的点和列 \(y\) 代表的点连边。
然后考虑现在问题变成了给定一个二分图,可以标记出一些点,但必须满足每个标记点都能找到一个邻边匹配,且找到的邻边互不相同。求标记点的方案数。
然后就考虑,如果一个连通块是不是树的话,那么任意标记方式都是合法的。具体构造可以直接提出一个基环树然后环点匹配环边,树上点匹配连到父亲的边。
如果一个连通块是树的话则发现除了全标记,其余的方案均合法,构造方法是选择一个未标记点当作根,然后所有标记点与连向父亲的边匹配。
1612F | 2800 | A
首先可以不管套件,假定 \(n<m\),那么答案不超过 \(\mathcal{O}(\log n+\frac{m}{n})\),也就是先倍增把 \(n\) 造出来,然后一步步造 \(m\).
答案这么小,那么常见的套路就是把答案放进复杂度里。
然后考虑一个 dp,假设当且在第 \(o\) 轮,令 \(f_i\) 为手中最牛逼的盔甲是 \(i\),能够拿到最牛逼的武器是 \(f_i\),想要 dp 出第 \((o+1)\) 轮的 \(f'\).
不用套件的转移,购买盔甲是 \(f'_j\gets f_i,i\leq j\leq i+f_i\),购买武器是 \(f'_i\gets f_i+i\).
用套件的话,那么一定是用 \(i\) 和 \(f_i\),要不然不会更优,所以也能类似转移。
通过打 tag 然后从后往前取 max 可以做到单次 \(\mathcal{O}(n)\) 转移,那么时间复杂度就是 \(\mathcal{O}(n\log n+m)\).
804D | 2500 | B
经典结论是,两个树加一条边连在及其的时候,新的直径的两个端点一定是原先两棵树直径的四个端点其中两个。令 \(f_x\) 为 \(x\) 与其所在子树直径端点的距离的最大值(实际上就是距离 \(x\) 最远的点),\(g_x\) 为 \(x\) 所在树的直径。如果连起一条边 \((u,v)\),那么新的直径就是 \(\max(g_u,g_v,f_u+f_v+1)\).
假如现在询问 \((u,v)\),令 \(len=\max(g_u,g_v)\),那么新的直径就是 \(\max(len,f_u+f_v+1)\).
那么枚举较小的那个连通通块的点,在第二个连通块里二分。这样复杂度是 \(\mathcal{O}(n\sqrt n\log n)\).大小 \(<\sqrt n\) 的不会超过 \(\mathcal{O}(n\sqrt n\log n)\),\(>\sqrt n\) 连通块,大小比它大的不超过 \(\sqrt n\) 个,这部分复杂度也是 \(\mathcal{O}(n\sqrt n\log n)\).
1379F2 | 2800 | B
首先每四个格子划分成一个方块,划分成 \(n\times m\) 个方块,并且每个方块中的左上角和右下角两个白格至多选一个。所以能够放国王的上界就是 \(n\times m\).
然后再考虑怎样一个放置方案是合法的,首先每行(这里是方块行)方块中,一定是一个前缀放左上,一个后缀放右下。假设第 \(i\) 行方块前 \(f_i\) 个放了左上,那么还要求 \(f_i\geq f_{i+1}\).不难发现这是一个充要条件。
然后考虑被 ban 掉的左上格会给 \(f_i\) 一个上界 \(r_i\),右下格则会给 \(f\) 一个下界 \(l_i\).考虑暴力贪心判定,就是当且仅当 \(r_i\) 的前缀 \(\min\) 比 \(l_i\) 要小。然后就是每次增删格子相当于修改一个 \(l\) 或者一个 \(r\).
那么用线段树维护一个分治信息,当前区间是否合法,当前区间 \(r\) 的 \(\min\),当前区间 \(l\) 的 \(\max\).每次合并左右两个区间的时候,如果左区间 \(r\) 的 \(\min\) 比右区间 \(l\) 的 \(\max\) 要小的话说明这个区间不合法。
时间复杂度是 \(\mathcal{O}(n\log n)\).
1310C | 2800 | D
首先是把所有子串拉出来然后排好序(可以用 Trie 来完成),然后二分答案。
对于一个字符串 \(t\),现在要计算生成的字符串字典序 \(>t\) 的方案数。
没想到的地方是,因为这里二分答案了,所以实际上不需要对所有可能的子串都求这个方案数,只需要对二分到的这些字符串求即可。
就设 \(f_{i,j}\) 为前 \(i\) 个字符,划分为 \(j\) 段,方案数是多少。暴力转移是 \(\mathcal{O}(n^2m)\) 的。这里是要求每一段划分出的字符串字典序都 \(>t\) 就能满足条件。
考虑从 \(f_{i,j}\) 向后转移,发现能够转移到的是一个后缀,也就是令 \(g_i\) 为最小的 \(k\) 满足 \(s[i,k]\) 比 \(t\) 大,那么 \(f_{i,j}\) 可以转移给 \(f_{k,j+1}\) 其中 \(k\geq g_{i+1}\).所以用差分优化转移即可。
单次 check 时间复杂度 \(\mathcal{O}(nm)\),总时间复杂度就是 \(\mathcal{O}(nm\log k)\).
1270F | 2600 | D
想到根号分治了,但是根号分治的东西错了。
搞个前缀和,然后就是要求 \(d(s_r-s_l)=r-l\),然后对 \(d\) 根号分治。
如果 \(d\leq B\),那么将式子化为 \(ds_r-r=ds_l-l\),那么开个哈希表记录相同的 \(ds_i-i\) 有多少个就可以了。复杂度是 \(\mathcal{O}(nB)\)
如果 \(d>B\),那么 \(s_r-s_l< \frac{n}{B}\),枚举 \(s_r-s_l=k\),然后再枚举 \(l\),满足 \(s_r-s_l=k\) 的一定是一个区间,可以双指针或者预处理点什么东西都能求出,那么 \(r-l\) 也是一个区间,看这个区间里面有多少个 \(k\) 的倍数,并且满足这个倍数 \(>B\),可以 \(\mathcal{O}(1)\) 算出。这部分复杂度是 \(\mathcal{O}(\frac{n^2}{B})\).
取 \(B=\mathcal{O}(\sqrt n)\),那么时间复杂度就是 \(\mathcal{O}(n\sqrt n)\).
1270H | 3300 | D
令 \(a_0=10^6+1,a_{n+1}=0\),将 \((i,a_i)\) 视作一个点,把折线图画出来。对于一个 \(a_i\) 如果 \(y=a_i-\frac{1}{2}\) 和折线只有一个交点,那么会产生 \(1\) 的贡献。
令 \(c_i\) 为 \(y=i-\frac{1}{2}\) 与折线图的交点个数,对于 \(a\) 中每两个相邻的数 \(x,y,(x<y)\),想大于给 \(c\) 的 \((x,y]\) 区间 \(+1\),然后对所有 \(c_i=1\) 的查询其权值之和(\(i\) 的权值就是有多少个 \(a_j=i\))
最头上有个 \(10^6+1\),最尾端有个 \(0\),所以 \(c\) 的最小值就是 \(1\),那么线段树记录最小值及其出现次数即可。
和洛谷题解本质相同,感觉是具象成了折线图更易于理解,但是反而会隐去思考过程,有意思。
1260F | 2700 | A
令 \(len_i=r_i-l_i=1\),\(all=\prod len_i\),那么一对点 \((x,y)\) 若都能染成颜色 \(c\),那么对答案的贡献就是 \(dis(x,y)\frac{all}{len_xlen_y}\).
对颜色编号维进行扫描线,现在问题就是每次将一个点染成黑色或者白色。求所有黑色点对 \((x,y)\) 的 \(dis(x,y)\frac{all}{len_xlen_y}\) 之和。
上点分树,就把 \((dep_x+dep_y)\frac{all}{len_xlen_y}\) 拆成 \(\frac{dep_xall}{len_x}\cdot \frac{1}{len_y}\) 和 \(\frac{all}{len_x}\cdot \frac{dep_y}{len_y}\),对于每个分治中心统计其作为分治中心的连通块中黑点的 \(\frac{1}{len_y}\) 和 \(\frac{dep_y}{len_y}\) 的和即可。要去重所以还需要统计分治中心的各个子树内部的。
时间复杂度 \(\mathcal{O}((n+r)\log n)\).
虽然是一只 log,但是常数好像特别大,也有可能是点分树写丑了。Code。
lyh 做法其实就是典中典 LNOI LCA 那道题,虽然联想到了那道题但是依然没想出来树剖做法。具体就是将 \(dis(x,y)\frac{all}{len_xlen_y}=dep_x\frac{all}{len_xlen_y}+dep_y\frac{all}{len_xlen_y}-2dep_{lca(x,y)}\frac{all}{len_xlen_y}\).前面两部分都可以记一个变量就行,最后一部分就用 LNOI LCA 那个套路上个树剖 + 线段树。
1254D | 2700 | B
一次修改会让 \(ans_v\) 加上 \(d\),让 \(v\) 的邻点 \(x\) 子树全加上 \(\frac{n-size_x}{n}d\).
考虑定根之后,批量处理儿子,单独处理父亲。批量处理儿子只需要对所有儿子上的 tag 值加 \(d\),单独处理父亲就是对子树补的答案整体加一个值。
然后询问一个点的答案的时候,首先是单独处理父亲那部分直接查树状数组值就行。还有就是所有 “ 祖先 \(x\) 的 tag 乘祖先 \(\frac{n-size_x}{n}\) ” 的和。这个还是不太好办,就考虑树剖,把这个 tag 值记在 \(x\) 的父亲 \(v\) 上(毕竟是 \(v\) 修改给它的),这样一条重链就可以直接单点加,区间求和,跳轻边可以直接查 tag.
复杂度是 \(\mathcal{O}(n\log^2 n)\),其中一个是树状数组的 \(\log\).
1223F | 2600 | ?
之前做 qazswedx 的模拟赛做到过(校内 OJ 链接),他给出的做法是 \([l,r]\) 合法当且仅当从前往后做括号匹配时 \([1,l-1]\) 的栈和 \([1,r]\) 的栈完全相同。由于栈只有 push_back 和 pop_back 所以可以哈希。用哈希表复杂度就是线性的了。
1220F | 2700 | B
先让 \(1\) 打头,然后删掉 \(1\).考虑剩余的序列,一定是从某个地方劈开一刀,前面的部分是 \(1\) 的右子树,后面部分是 \(1\) 的左子树。那么现在问题就是如果处理出每个前缀的笛卡尔树的深度(后缀是对称的)。
考虑笛卡尔树的构建过程,维护了一条权值 \(w\) 递增的右链,插入一个 \(w_y=v\) 的时候,找到这条链自底向上第一个 \(w_x<v\) 的节点 \(x\),然后 \(y\) 挂在 \(x\) 右儿子下,\(y\) 左儿子挂 \(x\) 原先的右子树。
考虑深度怎么变化,就是每次尾端插入一个值,区间 \(+1\),维护区间 \(\max\) 即可。
1188D | 3100 | D
对数位这种东西还是理解不够。性质和前面的 1322B 其实是一样的啊。
首先转化一下问题,如果要让它们都变成 \(x\),那么令 \(b_i=\max \{a_j\}-a_i\),则步数是 \(\sum \operatorname{pop\_count}(b_i+x)\).
然后现在考虑进位的问题,假设考虑了现在考虑到了第 \(i\) 位,和 1322B 的分析方式一样,按照 \((i-1)\) 位排序,那么产生进位的一定是一个后缀,则对第 \(i\) 位上的值记一个前缀和,就能根据上一次进位的数个数,算出当前这一位进位的个数,以及这一位留下来一个 \(1\) 的个数,这样就能数位 dp \(f_{i,j}\) 表示前 \(i\) 位有 \(j\) 个进位的最小答案是多少。
1142D | 2800 | D
是不是题面就把做法给透了(
考虑一个排名为 \(k\) 的数 \(x\) 后面拼上一个 \(c\) 之后得到的数排名,就是 \(9+\sum_{i=1}^{k-1}(i\bmod 11)+c+1\),\(9\) 是初始九个数,\(\sum\) 是所有比 \(x\) 小的标记点拓展出来的数,\(c+1\) 就是它在 \(x\) 能拓展出来的数中是第 \((c+1)\) 个。
然后排名只需要关注模 \(11\) 意义下的值。那么就 dp \(f_{i,j}\) 表示以 \(i\) 为结尾的串排名模 \(11\) 为 \(j\) 的有多少个即可。
1129D | 2900 | D
完全没想到根号,总以为是 polylog.
dp \(f_i\) 以 \(i\) 为结束的划分个数。扫描 \(i\),记录 \(c_j\) 为 \([j,i]\) 里面仅出线一次的数的个数,然后操作就是对 \(c\) 区间 \(+1\) 或者区间 \(-1\),查询前缀里面 \(c=k\) 的 \(f\) 和。
然后上一个分块,每个块内用个桶维护。整块直接移动指针(就是打 tag),散块暴力重构。复杂度 \(\mathcal{O}(n\sqrt n)\).直接开桶空间可能是 \(\mathcal{O}(n\sqrt n)\) 的,但是考虑 \(c\) 的实际含义,一个块内相邻 \(c\) 的差分的绝对值最多为 \(1\),那么一个块里的桶只需要开大小为 \(\mathcal{O}(\sqrt n)\) 即可,这样复杂度就是 \(\mathcal{O}(n)\).
1098D | 2800 | D
瞄了一眼大致思路然后尝试编一编。
暴力是,每次挑出最小的两个合并。
需要观察到没有产生贡献的次数很小。考虑最小的那个数的大小,如果一次合并没有产生贡献,那么最小的数至少 \(\times 2\).所以最多会有 \(\mathcal{O}(\log (qx))=\mathcal{O}(\log q+\log x)\) 次。
根据这个来观察还有什么性质,考虑如果一次合并没有产生贡献,那么那个时刻的最小值一定是所有数从小到大排序后的一段前缀合并出来的,并且发生合并的只有这个前缀。所以将所有鱼的重量 \(a\) 从小到大排序后,一个位置的前缀和 \(s_i\times 2 <a_{i+1}\) 时不会产生贡献。
最后是套路,每 \(2^i\) 分个块(\([2^0,2^1),[2^1,2^2),[2^2,2^3),\cdots\))。这样的从小到大排序后,“ \(s_i\times 2<a_{i+1}\) ” 也就是不合法的情况出现的位置一定是块与块之间。所以每个块开一个可删堆 / multiset
即可解决。时间复杂度是 \(\mathcal{O}(q\log x)\) 的。
Code.
1044F | 2700 | B
都点出是一个 dfs 树了,那就先从 dfs 树的结构上考虑。那么可以观察出一个点是好点当且仅当以这个点为根的 dfs 树上只有返祖边,没有横叉边。如果有横叉边的话,走到两个端点 LCA 处时,无论从哪里走都会使得走过这条横叉边;如果没有横叉边的话,优先走树边,这样非树边都是返祖边,一定不会被经过。
考虑加入一条边 \(u,v\) 后,哪些点在这条边的限制下是合法点,就是以 \(u,v\) 之间链上的点为根 \(u,v\) 各自的子树。如果钦定点 \(1\) 为根的话,那么剩余的合法点就是:
- \(u\) 是 \(v\) 的祖先(反之亦然):\(u\) 的子树补(包含 \(u\)),\(v\) 的子树;
- \(u,v\) 互为祖先:\(u\) 的子树和 \(v\) 的子树。
对每个点记录有多少条非树边不会限制它。把树拍成 dfs 序,那么问题就变成了区间加减,询问有多少个点值为 \(k\)(\(k\) 是非树边个数)。由于这个只一定 \(\leq k\),所以线段树记录区间最大值以及区间最大值出线次数即可。
917C | 2900 | D
每次只能将最左边的青蛙往右跳,所以青蛙所在的区间长度一定 \(\leq k\).看到数据范围基本上能猜出来是状压 dp 然后矩阵快速幂优化了。关键是怎么设计状态?
先考虑没有特殊石头的情况。令 \(f_{i,S}\) 为青蛙位于 \([i,i+k)\) 内,且存在状态为 \(S\),然后需要预处理处 \(w_{S,T}\) 表示从 \(f_{i,S}\to f_{i+1,T}\) 的花费,和 \(i\) 没有关系,可以直接预处理出。然后就可以 \((\min,+)\) 矩阵快速幂了。
如果有特殊石头,先矩阵快速幂到那个特殊石头前面,再在这个特殊石头处暴力转移过去,然后继续往后矩阵快速幂即可。
记 \(t=\binom{t}{x}\) 最大为 \(70\),就是矩阵的大小。和美食家那个题类似,只需要预处理出转移矩阵的 \(2^i\) 次幂,每次只需要 \(\mathcal{O}(t^2)\) 做 \(\mathcal{O}(\log n)\) 次向量乘矩阵即可。这样总复杂度就是 \(\mathcal{O}(t^3\log n+q(t^2\log n+tk))\).
看上去好像挺难写,学习一下 Alex_Wei 的写法。