T1:
每个数至多有$2 \sqrt{m}$个约数,也就是说即使$n$很大,在数集中有倍数的数在$m \sqrt{a}$级别。
可以暴力筛出数集中每个数得约数,用hash表维护一下,可实现$O(1)$查询。
用$n$减去筛出的数个数即为答案中0的个数,但是注意超过$n$的数要删掉。
时间复杂度$O(m \sqrt{m})$。
T2:
我们发现每个物品的花费很大,价值却很小,于是我们可以将价值做背包,求出每个花费对应的最小代价。
然后将询问离线排序,从时间较早的计划做起。
将所有商店也排序,从早到晚枚举计划,用单调指针维护当前可以买到东西的商店。
如果有新的商店加入,就再做一轮背包DP。
背包DP同时维护一个花费的后缀最小值,用于二分查询最大的可以获得的价值。
时间复杂度$O(n^3+mlogn)$。
T3:
最长路即是树上直径。
树上直径可以用并查集维护。
将两个连通块连接,新的直径一定是由原先两条直径的四点中的两点组成的。
在并查集的代表元素上维护直径的两个端点,合并时分6种情况讨论即可。
而这六种情况的长度都可以在原树上用lca或树剖求出。
但是次题还有上界限制,也就是说我们需要分离并查集。
用平衡树维护可持久化并查集?
考虑类似线段树分治的思路。
对于速度值域开一棵线段树,把每条边像线段树区间查询一样加进去,一条边至多被劈成$log(r-l)$段。
然后dfs遍历整棵线段树,到一个节点将这个节点的边加上,同时用栈维护加边前并查集的状态。
回溯时直接弹栈,将并查集连的边分离即可。
由于要维护结构,并查集不能用路径压缩,只能按秩合并。
处理出来所有答案,$O(1)$回答询问即可。
遍历线段树是$O(n)$的,而每次查询是$O(mlogn)$的,所以不能每次都查询。
由于一共有$m$条边,每条边至多被劈成$logn$段,外加并查集的复杂度$O(logn)$,总的时间复杂度为$O(nlog^2n)$。
原文地址:https://www.cnblogs.com/hz-Rockstar/p/11624798.html