钥匙
洛谷 P8339
钦定当有很多把钥匙能打开开宝箱时使用最后拿到的一把(应该要想想用第一把打开,实际不好做。)
每种颜色 \(col\) 的钥匙和宝箱是互相独立的,可以对每种颜色建出虚树。对于一把钥匙 \(u\),以它为根进行搜索,记录一个值 \(c\)。碰到颜色为 \(col\) 的钥匙 c++,碰到颜色为 col 的宝箱 c--,当到达 \(v\) 时 \(c\) 第一次变成 \(0\) 就说明 \(u\) 打开 \(v\) 了(这也时为什么钦定最后一把打开 \(v\),否则可能时 \(u\) 另一棵子树内的钥匙打开 \(v\))。
因为每种颜色只有 \(5\) 把钥匙,总共只有 \(5n\) 对 \((u, v)\)。
现在问题就转化为了 \(s\) 到 \(e\) 的路径会经过多少对 \((u, v)\),这和 HNOI2015 接水果 一样的。考虑 \((u, v)\) 会对哪些 \(s, e\) 做贡献,分讨一下 \(u, v\) 是否为祖孙关系,发现 \(s, e\) 的 dfs 序分别在 \(1/ 2\) 个区间内,转化为二维数点问题树状数组做即可。
时间复杂度:\(O((5n + m) \log (5n + m))\),常数还是不小的,似乎要把 \((u, v)\) 拆成至多 \(4\) 个算,注意空间。
总的来说,这个题要想到钦定哪把钥匙来开当前的宝箱(无非两种,都试试吧),然后建出虚树求出 \((u, v)\)。就转化为了一个子问题,做过原题还是不难想到接下来怎么做的,就是把它拍到 dfs 序上然后二维数点。
虚树可以利用在这种若干个互不干扰的问题上,这种路径包含问题转化为二维数点也是一个经典套路吧。