杂题20200626

CF958E2 Guard Duty (medium)

\(n\) 个物品,每个物品有权值 \(a_i\) ,选恰好 \(k\) 个物品,使得所选物品不相邻且权值和最小

\(k\leq5000;\ n\leq5\times10^5\)

可以发现最多只会使用 \(a_i\)\(3k\) 小的物品,剩下的可以丢掉

做法一:因此暴力 dp 是 \(O(k^2)\) 的,可以用 wqs 二分优化到 \(O(k\log a_i)\)

做法二:考虑可撤销贪心,大概是选了一个物品 \(i\) 后将这个物品的权值看做 \(a_{i-1}+a_{i+1}-a_i\) ,可以将 \(a_0,\ a_{n+1}\) 赋值为 \(\infty\) 以避免特殊情况,时间复杂度 \(O(k\log k)\)


ZOJ4064 Repair the Artwork

给定一个长度为 \(n\)\(0,\ 1,\ 2\) 构成的序列 \(a_i\) ,定义一次合法的操作为,选一段区间 \([l,\ r]\) 使得区间中不存在 \(1\) ,并将区间中所有数赋值为 \(0\) ,问执行 \(m\) 次合法操作后所有 \(2\) 都被赋值为 \(0\) 的方案数\(\bmod 10^9+7\) 的值

\(n\leq100;\ m\leq10^9\)

考虑容斥,钦定某些 \(2\) 最终没有被赋值,相当于这些 \(2\) 被看做了 \(1\) ,方案数为 \((\displaystyle\sum\frac{l_i(l_i-1)}2)^k\) ,其中 \(l_i\) 是序列每个由 \(0,\ 2\) 构成的极长连续段的长度

可以在序列首尾各添加一个 \(1\) 以方便考虑

考虑记 \(f_{i,\ j}\) 为考虑到序列前 \(i\) 个位置,上一个区间结束于 \(i\) ,已经确定的区间的权值 \((\displaystyle\sum\frac{l_i(l_i-1)}2)\)\(j\) ,此时的容斥方案数。转移枚举上一个区间结束位置即可,时间复杂度 \(O(n^4)\) ,答案即为 \(\displaystyle\sum f_{n,\ i}\times i^m\)

原题很卡常


CF1168C And Reachability

给定一个长为 \(n\) 的序列 \(a_i\) ,定义 \(y\) 是可以从 \(x\) 到达的,当且仅当:\(x<y\) ,且存在子序列 \(x=p_1<p_2<\cdots<p_k=y\) ,使得 \(a_{p_i}\& a_{p_{i+1}}>0\)

\(q\) 次询问,每次给定 \(x,\ y\) ,问是否能从 \(x\) 到达 \(y\)

\(n,\ q,\ a_i\leq3\times10^5\)

做法一:

有一个正确性显然的暴力

int val = a[x];
for (int i = x + 1; i <= y; i++) {
  if (val & a[i]) val |= a[i];
}
ans = bool(val & a[y]);

在线段树上的节点维护 \(v_i\) 表示在进入这个节点对应区间前 \(val=2^i\) ,离开这个区间后 \(val\) 的值

可以显然地向上合并与询问时查询答案,时间复杂度均为 \(O(n\log^2)\)

做法二:

\(jump_{i,\ j}\) 为最小的 \(p\ (p\ge i)\) ,使得 \(a_p\) 二进制位包含 \(2^j\) ,且 \(i\) 能到达 \(p\)

\(nxt_{i,\ j}\) 为最小的 \(p\ (p>i)\) ,使得 \(a_p\) 的二进制位包含 \(2^j\)

\(nxt\) 转移显然, \(jump_{i,\ j}=\displaystyle\min_{a_i\&k\neq 0}\{jump_{nxt_{i,\ k},\ j}\}\) ,正确性应该很好理解,预处理时间复杂度 \(O(n\log)\) ,单次询问判断 \(\min\{jump_{x,\ k}\}\leq y\) 即可, \(O(\log)\)


CF1163E Magical Permutation

给定集合 \(S\) ,找到最大的 \(k\) 使得存在一个 \([0,\ 2^k-1]\) 的排列 \(p\) 满足 \(p_i\operatorname{xor}p_{i+1}\in S\) ,输出一组方案

\(|S|,\ x\in S\leq2\times10^5\)

大概是用线性基乱搞……

找到一组数 \(v_i\) ,尽量多,且这些数能够组成 \([0,\ 2^k)\) 的一组基,考虑用这些数构造一个合法的 \([0,\ 2^k)\) 的排列

根据基的性质,暴力枚举 \(\operatorname{xor}\) 查分序列即可,即

void dfs(int now) {
  if (now > k) return;
  dfs(now + 1), d[++cnt] = v[now], dfs(now + 1);
}

注意所找的 \(v_i\) 必须满足 \(v_i<2^k\)


uoj207 共价大爷游长沙

给定一棵 \(n\) 个点的树,有 \(q\) 次操作:

  1. 删掉一条边,并加入另一条边,保证操作完后仍然是一棵树
  2. 向可重集合 \(S\) 中加入点对 \((x,\ y)\)
  3. 删除可重集合 \(S\) 中的一个点对
  4. 给定 \(u,\ v\) ,询问是否 \(S\) 中所有点对 \((x,\ y)\) 构成的 \(|S|\) 条链都包含了边 \(u\to v\)

\(n\leq10^5;\ q\leq3\times10^5\)

\(S\) 中每个点对 \((x,\ y)\) 附上一个随机权值后,用 lct 维护子树 xor 和,如果链 \((x,\ y)\) 两个端点均在同一个子树内,它们的影响会被 xor 抵消,因此操作 \(4\) 判断 \(v\) 的子树 xor 和是否等于 \(S\) 中每个 \((x,\ y)\) 的 xor 和即可

(终于学会了 lct 维护子树的正确写法(雾


CF765F Souvenirs

给定一个长为 \(n\) 的序列 \(a_i\)\(q\) 次询问区间 \([l,\ r]\) 中,满足 \(i\neq j\)\(|a_i-a_j|\) 的最小值

  • \(n\leq10^5\)
  • \(q\leq3\times10^5\)
  • \(a_i\leq10^9\)

做法一:

友 情 链 接

做法二:

考虑莫队

正常的莫队可能做不到 \(O(1)\) 拓展区间,考虑使用仅删除莫队,即对所有左端点在同一块内的询问,按右端点降序考虑,不断删除左 / 右端点元素,并支持撤销左端点的删除操作

用链表能够方便地 \(O(1)\) 维护一个存在的元素的前驱后继并删除元素(因为能够预知元素在链表中的下标),用数据结构维护答案即可

\(O(n\sqrt n)\) 次插入 / 删除元素, \(O(q)\) 次询问最小值

如果值域 \(10^5\) 可以直接用值域分块解决,现在有了一个 \(O(n\sqrt{10^9})\) 的优秀做法

注意到答案的上界是 \(\frac{10^9}{\text{询问区间长度}}\) ,暴力处理区间长度 \(<\sqrt n\) 的询问后,就可以得到 \(O(n^{\frac34}\sqrt{10^9})\)优秀做法

实际上也有与值域无关的做法

注意到删除元素对求集合元素 \(\min\) 是很不友好的,加入元素则不然

考虑将链表删除时对答案集合带来的信息更改离线下来。对于左端点在同一块内的询问,按右端点降序考虑维护链表并得到信息后,再按右端点升序维护答案,此时你能通过离线下来的信息得到一个元素应当被插入在链表中的位置,用它与前驱后缀的差的绝对值对答案取 \(\min\) 即可。每个询问 \(l\)\(l\) 所在块右端点 这段区间的贡献可以暴力在链表上加入并撤销影响

注意按右端点降序考虑时,所要得到的信息是:

  1. \(l\)\(l\) 所在块右端点,从左往右删除时在 \([l\) 所在块左端点\(,\ r]\) 的链表中的下标
  2. \(l\) 所在块右端点 到 \(n\) ,从右往左删除时在 \([l\) 所在块左端点$,\ \(当前节点\)]$ 的链表中的下标

时间复杂度 \(O(n\sqrt n)\) ,空间复杂度 \(O(n)\) ,但这玩意儿貌似并不怎么好写

posted @ 2020-06-26 21:00  cnJuanzhang  阅读(124)  评论(0编辑  收藏  举报