10 月做题记录
CF1879F *2800 \(\color{blue}\bigstar\)
一个点的答案是 \(h_i\times \left \lceil \dfrac{a_i}{x} \right \rceil\)。
一开始的想法是直接整除分块,然后分成 \(\sqrt{n}\) 个区间,扫描线一遍就可以 \(O(Tn\sqrt{a_i})\)。
但是没有保证 \(\sum n\),所以考虑一下 $\log $ 做法。
去想一下调和级数,枚举 \(x\),那么上取整相同的一定是一个区间,区间里面找最大值就可以了。
使用 st 表,复杂度 \(O(Tn\log n)\)。
CF1879E *2400 \(\color{blue}\bigstar\)
首先设 \(c_i\) 表示 \(i\) 连向父亲的颜色,那么显然 \(c_i\ne c_x\),\(x\) 是 \(i\) 的儿子。
因此想到深度相同的染一个颜色。
但这样会有一个问题,就是度数为 \(2\) 的点,无法分辨哪个是父亲。
考虑三种颜色,然后形成一个环,这样取两种颜色就可以钦定一个是父亲了。
CF1870F *2900 \(\color{green}\bigstar\)
长度相同,字典序单调。
分析一下,发现满足条件的是一个区间,可以二分,没了。
求字典序也是按长度分类求。
CF1866L *2400 \(\color{green}\bigstar\)
枚举 \(k\),然后每 \(N\) 个一段,总共有 \(M\) 段,每一段有一个后缀的盒子可以拿到,直接暴力就是 \(O(m^2)\) 的。
CF1284E *2500 \(\color{green}\bigstar\)
枚举 \(p\),找四个点,满足不存在相邻两个角大小超过 \(180\)。
容斥,减去全部在一半的方案。
卡精度,司马了。
const lb Pi=acos(-1.0l);
CF1497D *2500 \(\color{Gold}\bigstar\)
md,不会做。
空间很小,不能暴力 dp。
此时一个高妙做法是,把所有转移 \((i,j)\) 按 \(2^i-2^j\) 排序。
然后从小到大转移即可。
和图上单调上升路径比较像。
CF1487G *2700 \(\color{blue}\bigstar\)
最多三个不合法,去容斥。
容斥不用管具体是那两个字母,只需要关注选了多少个,因此直接暴力 dp,复杂度 \(O(n^3)\)。
CF1019D *2700 \(\color{Gold}\bigstar\)
枚举一条边,然后把其他点按理这个点距离排序,就可以二分。
然后发现把边极角排序,这样每次会改变两个点的相对顺序。
CF917C *2900 \(\color{green}\bigstar\)
状压,设 \(f_{i,S}\) 表示 \(i\),前面 \(k\) 个位置是否有人状态为 \(S\),每次让 \(S\) 第一位的人向前走即可。
有特殊点的位置暴力 dp。
代码有点难写。
CF1500E *3300 \(\color{blue}\bigstar\)
容易发现枚举集合大小 \(x\),相当于最小 \(x\) 个数和最大 \(x\) 个数构成一个区间,然后求并。
分析一下,发现 \(x\) 有一个前缀是区间两两不交,后缀是区间两两不交,中间的可以合并成一个大区间,并且 \(|S|/2\) 这个点一定在中间大区间里。
那么左右二分即可,复杂度 \(O(n\log^2 n)\)。
代码比较难写,还被卡常。
CF1103C *2700 \(\color{green}\bigstar\)
套路地跑个 dfs 树。
如果深度大于 \(n/k\) 就结束。
否则说明叶子数量至少为 \(k\)。
考虑对于每个叶子去找一个环,那么除了树边之外还有两条非树边 \((x,y),(x,z)\)。
那么就有三种选非树边形成环的方案,证明一下发现必然有一个满足条件。
CF1103D *3100 \(\color{blue}\bigstar\)
有趣题。
首先观察到 \(\gcd\) 的质因数很小,最多只有 \(M=11\) 个。
可以状压,考虑对于每个数,看它去把 \(S\) 这个集合的质因子扔掉是否合法。
怎么找这个集合呢,就直接把无关质因数去掉,会发现剩下的数肯定没多少,直接存结果就行。
然后相当于选一堆集合的并是全集。
但是每个数只能选一个区间,比较阴间,而且复杂度爆炸。
考虑有代价,并且最多选 \(M\) 个集合,因此对于每个 \(S\),只需要处理出前 \(M\) 小的代价的区间即可。
然后暴力,dp 的时候只要枚举子集。这样复杂度是 \(O(3^MM^2)\)。
看了一个有趣的技巧:对底栈。
有 \(n\) 种物品,求一个最小的区间满足里面的东西 0/1 背包后 \(f_m\ge v\)。
注意到这个东西加入容易,但是不支持删除。
显然这个区间满足双指针,但是左端点弹出难以维护。
考虑开两个栈,加入直接加入在右边的栈里,然后维护栈前缀的 dp 值,如果要弹出了,先把左边的栈顶不断弹出直到左边空了,然后把右边栈的元素全部放到左边栈里面去。
这样每个元素最多放两次,然后合并左右复杂度也是对的,总复杂度 \(O(nm)\)。
CF1416E *3200 \(\color{blue}\bigstar\)
垃圾题,平衡树暴力维护 dp。
CF1886F \(\color{blue}\bigstar\)
CF1120F *3100 \(\color{green}\bigstar\)
???
如果开始存信了,信箱一直都会被使用,相当于每个连续段第一个的贡献会去掉。
从后往前枚举第一个存信的位置,然后每个点贡献加起来就行。
CF1120E *2600 \(\color{blue}\bigstar\)
爆搜题。
CF1129E *3100 \(\color{blue}\bigstar\)
D 做太久了,本来这题能过的。
先考虑可以问出什么基本信息,发现一头为 \(1\),中间放 \(x\),另一头放剩下所有,这样可以问出 \(x\) 的子树大小。
然后考虑从根出发,确定儿子,注意到中间放 \(x\),然后两端是 \((1,y)\) 可以判断 \(y\) 是不是 \(x\) 子树内的点。
二分一下,子树大小最大的 \(y\) 一定就是 \(x\) 的儿子。
然后把 \(y\) 去掉,递归 \(y\) 即可。
询问次数 \(O(n\log n)\)。
CF1129D *2900 \(\color{blue}\bigstar\)
套路题。
直接 dp,然后相当于要维护一个区间加减,查询 \(\le k\) 的位置的 dp 值之和。
大力分块即可。
abc311_h *3605 \(\color{red}\bigstar\)
牛逼题,最新科技。
lg 题解各种复杂度的分析漏洞百出,最好还是读官方题解。
就是带约束的树上背包,那么肯定不能合并做,因为合并复杂度 \(O(m)\)。
然后想到了约束背包经典题目,古典做法就是把 dfs 序跑出来,然后在 dfs 序上 dp,一个点如果不选了那么它的子树就都不会选了,直接跳到下一个地方,否则就向子树跳一个,这样的话只需要支持背包加入一个物品,那么复杂度就是 \(O(nm)\)。
如果跑出任意一个连通块,需要加一个点分治,但是子树内答案就很难做(好像不可做)。
然后有了一个最新科技,感觉完全吊打 dfs 序上 dp。
dfs 序上 dp 的本质是,类似建一个自动机,每个点连向第一个儿子或者子树外的第一个点,然后自动机上的一条路径就代表了一个树上包含根的连通块,然后在这个上面 dp。
但是这个东西和子树内信息非常割裂,而且只能维护树上连通块,遇到树上非联通块的约束条件就萎了,就是 dp 到一个点,父亲的信息是未知的。
此时一个很牛的考虑是,把 dfs 到 \(i\) 的背包信息传到儿子里面去 dp,然后再传出来。
function dfs(c, dp)//贺的
Dp[0] <- dp
Dp[1] <- dp
for d in {children of c} do
Dp[0] <- dfs(d, Dp[0])[0]
Dp[1] <- dfs(d, Dp[1])[1]
end for
for i = 0 to X - W[c] do
chmax(Dp[C[c]][i + W[c]], Dp[C[c] ^ 1][i] + B[c])
end for
return Dp
由于外部点怎么选对我其实是没有影响的,只需要把外面的 dp 数组搬过来直接做,约束条件可以在传回父亲的时候进行限制即可。
然后每次传回父亲的时候再将这个点加入。
注意到这个 dp 复杂度是 \(O(m2^n)\),很不牛。
注意到复杂度劣主要是因为每个子树都分成 0/1 两种情况分别 dp,注意到如果只有一个儿子,那么可以把儿子的 dp 状态值直接继承上来,因为没有其他儿子的约束。
首此启发,考虑一个类似 dsu on tree 的东西,先递归重儿子,把信息直接继承。
function dfs(c, dp)
if c is not leaf then
h = (heavy child of c)
Dp <- dfs(h, dp)
for d in {children of c} / {h} do
Dp[0] <- dfs(d, Dp[0])[0]
Dp[1] <- dfs(d, Dp[1])[1]
end for
else
Dp[0] <- dp
Dp[1] <- dp
end if
for i = 0 to X - W[c] do
chmax(Dp[C[c]][i + W[c]], Dp[C[c] ^ 1][i] + B[c])
end for
return Dp
分析一下复杂度。
如果儿子有 \(k\) 个,那么最大值一定是 \(a_1=a_2=...=a_k\)。
显然 \(k=2\) 最优,复杂度为 \(O(n^{\log_2 3}m)=O(n^{1.59}m)\)。
但是注意到现在得到的这个 dp 还是只能求出整棵树的答案,不能求出每个子树的答案,因为每次递归都是把外面的信息传进去。
求一个点的信息,只要传一个空的状态进去就行了,但是复杂度乘个 \(n\)。
这个也可以 dsu,把空信息传到重子树里面,然后整个传回来,然后扔到轻子树里面去 dp。
复杂度类似的分析一下,发现和求单次是一样的。
P6326 \(\color{green}\bigstar\)
就是上面的典题了,可以点分治,但是不如把每个子树的答案求出来求最大值,复杂度 \(O(nm\log n)\),比点分治好写,常数还小。
loj2523 \(\color{green}\bigstar\)
\(\gcd\) 满足即可,因此简单反演一下。
loj2719 \(\color{green}\bigstar\)
每次把一个最小的扔到最前面,可以发现相当于一个数 \(x\) 如果在操作它之前到达了 \(x\) 这个位置的右边,那么肯定到达不了下界。
分析一下这个条件,相当于一个数前面有比他大的,后面有比他小的就寄,所以最长下降子序列不超过二。
那么无限制 dp,考虑暴力是记录前面下降子序列长度为 \(1\) 的,最大是多少,长度为 \(2\) 的,最大为多少。
然后发现长度为 \(1\) 就是前面的最大值,长度为 \(2\) 的必须是没取过的最小值,不然不合法。
因此可以得到一个 \(O(n^2)\) dp,设一个最大值。
画一个网格图,然后 \(j<i\) 都是似掉的状态。
可以发现是一个卡特兰数一样的东西,因此可以 \(O(1)\) 爆出点值。
然后字典序的问题,直接找到第一个大的位置就行。
复杂度 \(O(n)\)。