是19年四五月看的题,但咕咕了
(Description)
给定一棵树,在树上每个点处有(1)个人,每个人有一个忍耐程度(t_i)。当一个人子树内放假的人数(gt t_i)且他没有放假的时候,他会删库跑路。初始时所有人都没放假。有(m)次操作,每次将一个人由放假变为不放假或由不放假变为放假,然后输出一共有多少个人会删库跑路。
(n,mleq10^5, 0leq t_ileq n)。
(Solution)
(Sol1)
令(A_i=t_i-x),(x)是(i)子树内有多少人放假了。就是维护(A_ilt0)且没有放假的人的个数。
树剖+分块。对DFS序分块。记(s[i][j])为第(i)块内(A_x=j)且没放假的人的个数。每次修改只会影响一个位置的值,很容易维护。
一个空间上的优化是,记(tag[i])为第(i)块的整体加标记。我们限制(s)的第二维在([-D,D])范围内(只在这个范围内统计),这样空间就是(O(块数*D))的。因为(|tag[i]|lt D)时显然不会有(x)影响答案。而当(taggeq 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(nsqrt n))。
代码咕了(两年半了),见 http://codeforces.com/contest/966/status/E?order=BY_CONSUMED_TIME_ASC