CF. 966E. May Holidays(树剖/虚树 分块)
是19年四五月看的题,但咕咕了
\(Description\)
给定一棵树,在树上每个点处有\(1\)个人,每个人有一个忍耐程度\(t_i\)。当一个人子树内放假的人数\(\gt t_i\)且他没有放假的时候,他会删库跑路。初始时所有人都没放假。有\(m\)次操作,每次将一个人由放假变为不放假或由不放假变为放假,然后输出一共有多少个人会删库跑路。
\(n,m\leq10^5,\ 0\leq t_i\leq n\)。
\(Solution\)
\(Sol1\)
令\(A_i=t_i-x\),\(x\)是\(i\)子树内有多少人放假了。就是维护\(A_i\lt0\)且没有放假的人的个数。
树剖+分块。对DFS序分块。记\(s[i][j]\)为第\(i\)块内\(A_x=j\)且没放假的人的个数。每次修改只会影响一个位置的值,很容易维护。
一个空间上的优化是,记\(tag[i]\)为第\(i\)块的整体加标记。我们限制\(s\)的第二维在\([-D,D]\)范围内(只在这个范围内统计),这样空间就是\(O(块数*D)\)的。因为\(|tag[i]|\lt D\)时显然不会有\(x\)影响答案。而当\(tag\geq D\)时,暴力重构这个块即可。
\(Sol2\)
虚树+分块。对询问分块,每次处理\(O(B)\)个询问。容易发现树被分成了\(O(B)\)条链,同一条链上的点,被修改的值是相同的。
我们可以\(O(B)\)建出虚树。考虑如何回答每次询问。设\(A_i=t_i-x\),把每一条链上的点先按\(A_i\)排序,然后维护一个指针,表示当前第一个\(\lt0\)的位置在哪。将重复的\(A_i\)合并到一起,每次更新一条链最多只会移动一下指针,是\(O(1)\)的。排序可以用基数排序。
处理完\(B\)个询问后更新一下所有\(A_i\)即可。
复杂度\(O(\frac{n^2}{B}+nB)\),也就是\(O(n\sqrt n)\)。
代码咕了(两年半了),见 http://codeforces.com/contest/966/status/E?order=BY_CONSUMED_TIME_ASC
很久以前的奇怪但现在依旧成立的签名
attack is our red sun $$\color{red}{\boxed{\color{red}{attack\ is\ our\ red\ sun}}}$$ ------------------------------------------------------------------------------------------------------------------------