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

分析一下复杂度。

\[f(n)=\sum_{\sum a_i=n-1} f(a_1)+2\sum_{j=2} f_{a_j}+O(m)\\ a_1=\max a_i \]

如果儿子有 \(k\) 个,那么最大值一定是 \(a_1=a_2=...=a_k\)

\[f(n)=f(n/k)(2k-1)+O(m)\\ =n^{\log_k 2k-1}m \]

显然 \(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,设一个最大值。

\[f_{i,j}=f_{i+1,j}+f_{i,j+1} \]

画一个网格图,然后 \(j<i\) 都是似掉的状态。

可以发现是一个卡特兰数一样的东西,因此可以 \(O(1)\) 爆出点值。

然后字典序的问题,直接找到第一个大的位置就行。

复杂度 \(O(n)\)

posted @ 2023-10-08 07:42  houzhiyuan  阅读(41)  评论(0编辑  收藏  举报