模拟赛8.16 解题报告
T1. 文件改名
题意:有 \(n\) 个文件,文件名分别为 \(s_{1...n}\),\(s_{1...n}\) 两两不同。小 \(\text{C}\) 想要把文件名改成 \(t_{1...n}\),\(t_{1...n}\) 两两不同,改名有顺序,在任何时候不能存在两个文件的名字相同。求小 \(\text{C}\) 最少要改多少次名。\(1\le n\le10^5,\space 1\le|s_i|,|t_i|\le10\)
考虑图论,把每个字符串映射成一个点,从 \(s_i\) 向 \(t_i\) 建一条有向边。由于 \(s_{1...n},t_{1...n}\) 各两两不同,所以一个点最多有一条入边、一条出边。
我们可以判断出来,这样的图中,每个连通块要么是一条链,要么是一个点数 \(\ge2\) 的环,要么是一个点自环。
-
连通块是一条链:因为是有向的,点从后往前逐一向后推进即可。操作数等于边数(文件个数)。
-
连通块是一个点数 \(\ge2\) 的环:另其中一个文件改成乱码,然后就断环为链,再把乱码改回来。操作数等于边数(文件个数)\(+1\)。
-
连通块是一个点自环:无需改名。
用并查集维护即可,时间复杂度 \(O(n\alpha(a))\)。
T2. 怪物猎人
题意:有 \(n\) 个怪物,每个怪物 \(i\) 有两个属性 \(a_i,b_i\)。有 \(m\) 名骑士,第 \(j\) 名的血量为 \(h_j\)。一名骑士同一时间只能打一个怪物,而且打死一个才能换下一个。打第 \(i\) 个怪物会掉 \(a_i\cdot b_i\) 的血,每打完一只怪物就会令其他仍存活怪物 \(j\) 的两个属性各加 \(d\):\(a_j\leftarrow a_j+d,b_j\leftarrow b_j+d\)。求对于每个骑士,他能打死多少怪物(每个骑士互相独立)。\(1\le n\le 3000,\space 1\le m\le3\times10^5,\space 1\le a_i,b_i\le10^6,\space 1\le h_j\le 10^{15},\space 1\le d\le200\)
若在打第 \(i\) 个怪物前已经打死了 \(x\) 个,那么本次掉血:
如果我们已经确定了要打的怪物集合 \(S\),那么我们已经能确定 \(\sum a_i\cdot b_i\) 和 \(\sum x^2d^2\)。我们一定是按 \((a_i+b_i)\) 从大到小打,这样能做到代价最小。
现在的问题是如何选择 \(S\),由于 \(n\le3000\),我们可以直接 \(\text{DP}\)。设 \(f[i,j]\) 表示按 \((a_i+b_i)\) 从大到小排序后,前 \(i\) 个怪物中打 \(j\) 个所需要的最小血量。
-
打第 \(i\) 个怪物:\(f[i,j]=f[i-1,j-1]+(a_i+(j-1)\cdot d)(b_i+(j-1)\cdot d)\)
-
不打:\(f[i,j]=f[i-1,j]\)
对于每个骑士,由于 \(f[n,0...n]\) 单调上升,直接二分即可。
T3. 魔术帽游戏
题意:有 \(n\) 个魔术帽,有 \(m\) 次交换,第 \(i\) 次为交换 \(a_i,b_i\) 这两个帽子。有 \(q\) 次询问,每次给出 \(x,l,r\),把球藏在第 \(x\) 个魔术帽,求经过第 \(l...r\) 次交换后球会到达哪个位置。\(1\le n,m,q\le2\times10^5\)
我们所需要维护的信息很少,没有数值计算,只需要记录位置。
子任务 \(l=1\) 给了点提示,当所有 \(l=1\) 时,我们可以直接离线按 \(r\) 排序,维护 \(g_i\) 表示原来 \(i\) 号帽子现在的位置,\(d_i\) 表示现在位置为 \(i\) 的帽子原来在哪里。
当 \(l>1\) 时,我们可以尝试充分利用以后信息。在 \(l-1\) 时刻,我们记录下 \(d_x\) 的值,即在 \(l-1\) 时刻位置为 \(x\) 的帽子的原来的位置,设 \(d_x=y\)。在 \(r\) 时刻,我们发现 \(g_y\) 就是我们的答案。
于是离线排序后双指针就行了,\(O(q\log q+n)\)。
T4. 计算树
题意:一棵 \(n\) 个点的树,树上每个点有个数字 \(a_u\),每条边有个运算符 \(opt_i(opt_i\in \{+,-,\times\})\),运算符的优先级都相同。\(q\) 次询问,每次给出 \(u,v(1\le u,v\le n)\),求 \(u\rightarrow v\) 路径连起来的表达式的值模 \(19491001\) 的值。
有史以来最简单 \(\text{T4}\)。
考虑树上倍增,维护 \(f_{u,i,0/1}\) 表示从 \(u\) 往上跳 \(2^i\) 步后的加/乘标记,\(g_{u,i,0/1}\) 则表示从 \(fa[u,i]\) 往下跳到 \(u\) 的标记。
然后找个 \(\text{LCA}\) 就行了,时间复杂度 \(O((n+q)\log n)\)。