2020北大集训题解

能听懂的就尽力写吧。代码肯定是都咕了。

Day 1

T1

再次感谢出题人不杀之恩,给了我 89 分这个鬼畜分数。

做法(不是写法)和正解完全一样,然而还是不知道自己怎么错了。

心情好了再去看 https://uoj.ac/problem/427 。这题不仅被甩了 11 分还花了 3h ……

T2

真就联赛模拟题啊 /jk

获得成就:上台讲题。

先考虑一个区间怎么做。对于每个位置直接处理出 \(pre_i\) 表示往前第一个不被包含在它的子树内的位置,可以线段树二分求出。然后就是一个二维数点。

多个区间的话,相当于给这个区间加了一个额外限制,即必须是某个点 \(L\) 的祖先。同样先线段树二分,得到第一个满足该条件的点(然而写这句话的时候发现这好像不太好做,所以要线段树二分出这个区间的一个前缀,使得前缀的 \(lca\) (好像 \(lca\) 也不好搞,只能是关于 dfs 序的某些东西)是 \(L\) 的祖先。所以我场上写的线段树二分可能复杂度还是错的???)。此时,前缀的所有点都一定不满足条件,而对于后面的点,如果子树中能包含这个前缀,自然也就能包含 \(L\)

线段树上只维护 \(dfn,low\) 。复杂度 \(O((m+Q)\log m)\)

T3

挺有趣的一道题,可惜场上没时间想。

称 x-度点 表示有 \(x\) 个儿子的点。

subtask 1

在这档部分分中,树中不存在一度点。

容易发现询问 \(V\backslash\{x\}\) 可以得到这个点是否是叶子,那么先用 \(n\) 次操作得到此时的叶子集合。然后考虑怎么确定叶子的父亲,发现当 \(x\) 是叶子时可以询问 \(V\backslash \{x,y\}\) 来确定 \(y\) 是否是 \(x\) 的父亲。如果 \(fa_x=y\) 那么会返回 \(n-2\) ,否则是 \(n-1\)

然后发现这个枚举 \(y\) 的过程其实可以直接二分一个前缀,就可以在 \(O(\log n)\) 的时间内得到一个叶子的父亲。发现两个叶子的父亲相同之后就可以删掉这两个叶子,并把它们的父亲加入叶子集合。

这样就获得了一个 \(O(n\log n)\) 次询问得到树形态的做法。

subtask 2

此时树中可能存在一度点,但询问次数更多(当然数量级没有变)。

考虑上面的做法,会发现在第一步就直接错了:询问 \(V\backslash \{x\}\) 并不能区分零度点和一度点。我们先把二度点区分出来,然后考虑零度点和一度点的集合 \(S\) 。那么询问 \(S\backslash \{x\}\) 会有两种取值:

  • \(n-2\) :这个点是零度点,且它的父亲是一度点。
  • \(n-1\) :这个点是一度点,或者它是零度点且它的父亲是一度点。

容易发现如果出现 \(n-1\) 的第二种情况,那么一定无法确定树的形态。所以先不考虑这一种情况,求出三种点的数量 \(c_0,c_1,c_2\) 。由于真正的 \(c_0\) 应当等于 \(c_2-1\) ,所以如果不满足这个条件就直接返回无法确定。

然后用 subtask 1 的做法先得到只考虑零度点和二度点的树形态,然后尝试把一度点塞进去。

然后又是一个性质:在二叉树中,询问一个点集,可以根据大小来判断是否至少有一组 祖先-后代 关系。那么选择一个叶子集合 \(L_0\) 和一个一度点 \(x\) ,询问 \(L_0\cup \{x\}\) 即可确定 \(x\) 的子树中是否存在 \(L_0\) 中的叶子。如果总叶子集合是 \(L\) ,那么询问 \((L\backslash L_0)\cup\{x\}\) 就可以知道 \(x\) 子树的叶子集合是否被 \(L\) 完全包含。

现在树的基本形态已经确定了,所以可以边分治,确定所有一度点是在这个子树内,在外面,还是恰好在这条边上。

出题人用神奇分析方法得到了询问次数上界是 10408 ,不太懂怎么做到的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章